2013-03-11 29 views
6

考虑下面的类如何将引用对象压扁为引用者的两个json.net属性?使用newtonsofts json.net像这样</p> <pre><code>public class User { public virtual int Id {get;set;} public virtual string Name {get;set;} public virtual User Superior {get;set;} } </code></pre> <p>我的目标是这个序列化JSON作为:

{ 
    Id: 101, 
    Name: 'Mithon', 
    SuperiorId: 100, 
    SuperiorName: 'TheMan' 
} 

我为什么要这么做?因为我想将Json用作我的DTO,而不生成动态对象的中间层。生成DTO应该按照惯例动态完成,而不是明确地,imho。我知道有些人可能会强烈反对,但讨论我的做法是不言而喻的。我只想知道是否以及如何完成。

挑战在于对Superior属性使用JsonPropertyAttribute只会产生一个属性作为输出,我需要两个属性。如果我使用JsonObjectAttribute,我将得到一个嵌套属性,并且我也会遇到顶级用户也被压扁的问题。

幸运的是,似乎json.net库中有足够的受保护和/或公共属性和方法,我可以扩展某些内容以获得所需的结果。那么问题是我应该从哪些类和方法开始,以获得我想要去的地方?从DefaultContractResolver派生,并重写GetProperties方法是好地方,还是我应该看看其他地方?

回答

2

简短的回答是肯定的,那将是适当的地方开始。这里是我结束了(现在):

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来表示我的IdName

对于我的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; 
} 

而且急,我所有的清洁实体和动态序列化的血淋淋的细节被封装,遗憾的是在两个地方,但它不能,因为我得到帮助不想从串行器访问我的数据源。