2013-03-11 72 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; 
} 

而且急,我所有的清潔實體和動態序列化的血淋淋的細節被封裝,遺憾的是在兩個地方,但它不能,因爲我得到幫助不想從串行器訪問我的數據源。

相關問題