public class User { public virtual int Id {get;set;} public virtual string Name {get;set;} public virtual User Superior {get;set;} }
我的目标是使用newtonsofts json.net将其序列化为json,如下所示:
{ Id: 101,Name: 'Mithon',SuperiorId: 100,SuperiorName: 'TheMan' }
我为什么要这样做?因为我想使用Json作为我的DTO而不生成动态对象的中间层.生成DTO应该按惯例动态完成,而不是明确地,imho.我知道有些人可能会强烈不同意这一点,但讨论我的方法除了重点之外.我只想知道是否以及如何做到这一点.
挑战是使用JsonPropertyAttribute作为Superior属性只会产生一个属性作为输出,我需要两个属性.如果我使用JsonObjectAttribute,我将得到一个嵌套属性,我会遇到顶级用户也被弄平的问题.
幸运的是,似乎json.net库中有足够的受保护和/或公共属性和方法,我可以扩展一些东西以获得所需的结果.那么问题是我应该从哪些类和方法开始到达我想去的地方?将从DefaultContractResolver派生,并覆盖GetProperties方法是好地方,还是我应该寻找其他地方?
public class MyContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type,MemberSerialization memberSerialization) { var properties = base.CreateProperties(type,memberSerialization); foreach (var pi in type.GetProperties().Where(pi => typeof (Entity).IsAssignableFrom(pi.DeclaringType) && Attribute.IsDefined((MemberInfo) pi,typeof(IdNameMemberAttribute)))) { properties.Add(CreateReferenceProperty(pi,Reflect.GetProperty<Entity>(e => e.Id))); properties.Add(CreateReferenceProperty(pi,Reflect.GetProperty<Entity>(e => e.Name))); } return properties; } private JsonProperty CreateReferenceProperty(PropertyInfo reference,PropertyInfo referenceMember) { var jsonProperty = base.CreateProperty(reference,MemberSerialization.OptOut); jsonProperty.PropertyName += referenceMember.Name; jsonProperty.ValueProvider = new ReferencedValueProvider(reference,referenceMember); jsonProperty.Writable = false; return jsonProperty; } }
IdNameMemberAttribute只是一个空属性,我用它来注释我要序列化的引用属性.重要的一点是,我不会使用Json.NET识别并用于生成JsonProperty的任何内容对其进行注释.这样我就不会从我的CreateProperties中得到重复的JsonProperty.
或者,我可以从DataMemberAttribute派生并查找,修改和克隆JsonProperty以表示我的Id和Name.
对于我的asp.net web api,我将此MyContractResolver设置为JsonFormatter.SerializerSettings的ContractResolver.
所以这使我得以进行序列化.对于反序列化,我有一个自定义ChangeSet对象,我存储PropertyInfo和对象.在反序列化期间,我确保保留Ids,然后解决来自我的数据存储的问题,在我的情况下使用自定义ActionFilter来访问数据存储会话.
这是我的序列化的本质:
var jsonSource = streamReader.ReadToEnd(); var deserializedObject = JsonConvert.DeserializeObject(jsonSource,type,SerializerSettings); var changeSet = deserializedObject as PropertyChangeSet; if (changeSet != null) { var jsonChange = JObject.Parse(jsonSource)["Change"].Cast<JProperty>().ToArray(); IDictionary<string,int> references = jsonChange.Where(IsReferenceId).ToDictionary(t => t.Name.Substring(0,t.Name.Length - 2),t => t.Value.ToObject<int>()); changeSet.References = references; var properties = jsonChange.Where(jp => !IsReferenceId(jp)).Select(t => t.Name).ToList(); changeSet.Primitives = properties; }
而且,我的干净实体和动态序列化的所有血腥细节都被封装,遗憾地在两个地方封装,但由于我不想从序列化程序访问我的数据源,所以无法帮助它.