2014-02-17 73 views
3

我創建了一個名爲Other的簡單類的測試控制檯應用程序。作爲練習,我想在序列化時將OtherString屬性設置爲null。我知道如何使用自定義的ContractResolver來做到這一點。我需要能夠用自定義轉換器來做到這一點。如何在WriteJson中刪除屬性

我寫的第一個轉換器非常簡單,我認爲它應該是。但是,它會拋出一個「使用類型'JsonContractandConvert.Models.Other'檢測到的自引用循環。」例外。在做了一些閱讀後,我做了一些改變,現在我有一個工作轉換器。這些變化更加冗長,但工作。

我的問題是爲什麼這些更改是必需的,是否有更好的方法來做到這一點與轉換器?

其他類:

[JsonConverter(typeof(OtherConverter))] 
public class Other 
{ 
    public int Id { get; set; } 
    public string OtherString { get; set; } 
    public int OtherInt { get; set; } 

public string OtherName 
    { 
     get 
     { 
     return "Other Name = " + this.OtherString; 
     } 
    } 
} 

第一次嘗試:(這一個拋出異常)

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    if (!this.CanConvert(value.GetType())) return; 

    var entity = value as Other; 
    if (entity == null) return; 

    entity.OtherString = null; 

    serializer.Serialize(writer, entity); 
} 

第二次嘗試:(此按預期工作)

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    if (!this.CanConvert(value.GetType())) return; 

    var entity = value as Other; 
    if (entity == null) return; 

    entity.OtherString = null; 

    writer.WriteStartObject(); 
    var props = entity.GetType().GetProperties(); 
    foreach (var propertyInfo in props) 
    { 
     var ignorAttribute = 
      propertyInfo.CustomAttributes.FirstOrDefault(i => i.AttributeType == typeof(JsonIgnoreAttribute)); 

    if (ignorAttribute != null) continue; 
       var tempVal = propertyInfo.GetValue(entity); 
      if (tempVal == null) continue; 

      writer.WritePropertyName(propertyInfo.Name); 
      serializer.Serialize(writer, tempVal); 
     } 
    } 
    writer.WriteEndObject(); 
} 

編輯:

這是我用來測試的控制檯應用程序的代碼。

class Program 
{ 
    static void Main(string[] args) 
    { 
     var otherObj = new Other { Id = 123, OtherInt = 456, OtherString = "This is the other string"}; 
     var json = JsonConvert.SerializeObject(otherObj, Formatting.Indented); 
     Console.WriteLine(json); 
    } 
} 
+0

public class ShallowCopyCollectionConverter<TCollectionType, TCopyType> : JsonConverter where TCollectionType : IEnumerable<TbdEntity> where TCopyType : TbdEntity, new() { public override bool CanConvert(Type objectType) { return typeof(TCollectionType).IsAssignableFrom(objectType); } public override bool CanRead { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (this.CanConvert(value.GetType()) == false) return; var entities = (TCollectionType)value; writer.WriteStartArray(); foreach (var entity in entities) { serializer.Serialize(writer, entity.ShallowCopy<TCopyType>()); //ShallowCopy<> is a method in the base class that all of my classes extend. } writer.WriteEndArray(); } } 

這人會用具體的類型轉換的抽象。 '{a = 1}'和'{a = 1,b = null}'有什麼區別?在讀者網站上,兩個json都將被轉換爲'b = null' –

+0

JsonIgnore只有在你想忽略它時纔會起作用。在上面的例子中,如果我設置了entity.OtherString =「其他」;我犯了同樣的錯誤。 – Jeff

+0

'我得到同樣的錯誤',什麼錯誤?這是一個簡單的序列化。 'JsonConvert.SerializeObject(obj)'就是這樣。在你的問題中不需要任何代碼(或任何轉換器)。 –

回答

1

回答我的問題...(我想我終於得到了我周圍的Json轉換頭)

要回答我的基本問題,爲什麼一個工作方式和其他沒有。我認爲答案只是這種行不通的方式是錯誤的。從我可以告訴,你需要使用作家對象。如果你不這樣做,它不會工作。 (可能有些情況並非如此,但我從未找到過。)

如果您只想從Json中刪除屬性,請使用[JsonIgnore]。 (簡單吧?)

如果你想改變一些值,你需要使用一個轉換器。通過使用Converter,您負責序列化Converter處理的任何對象。所以如果對象是一個像String這樣簡單的類型,或者是一個Int,那真的很簡單。如果它像數組或複雜對象那樣更復雜,那麼它需要更多的計劃。

這裏有一個簡單的字符串轉換的例子:

類連載:

public class Account 
{ 
    public int Id { get; set; } 
    public string AccountName { get; set; } 

    [JsonIgnore] 
    public virtual Account DefaultAssignTo { get; set; } 
    public int? DefaultAssignToId { get; set; } 

    [JsonIgnore] 
    public virtual ICollection<Role> Roles { get; set; } 

    [JsonIgnore] 
    public virtual Other Other { get; set; } 
    public int? OtherId { get; set; } 

    [JsonConverter(typeof(StringConverter))] 
    public string OtherName 
    { 
     get 
     { 
       return "Name = " + this.AccountName; 
     } 
    } 

}

轉換器:

public class StringConverter : JsonConverter 
{   
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(string).IsAssignableFrom(objectType); 
    } 

    public override bool CanRead 
    { 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     if (!this.CanConvert(value.GetType())) return; 

     writer.WriteValue("blah blah blah");  
    } 
} 

運行:(正如你可以看到我對我的數據使用實體框架)

using (var db = new Context()) 
{ 
    var json = JsonConvert.SerializeObject(db.Accounts.FirstOrDefault(), Formatting.Indented, 
     new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); 
    Console.WriteLine(json); 
} 

返回:

{ 
    "Id": 43, 
    "AccountName": "John", 
    "DefaultAssignToId": 43, 
    "OtherId": 19, 
    "OtherName": "blah blah blah" 
} 

一個是搞砸了我一個位的事情是,當使用writer.WriteStartObject()。基本上,如果你正在轉換一個複雜的對象,你需要使用它。如果你這樣做,那麼你需要創建所有的屬性名稱和值。在OP中,您可以看到我如何做到這一點的一個例子。最大的不利之處在於任何Json屬性的屬性裝飾都不會自動發生。所以如果可以的話,裝飾這個類並讓序列化器處理它。如果你錯誤地使用它,這是一個有趣的問題。

在字符串轉換器取代:

writer.WriteValue("blah blah blah"); 

有:

writer.WriteStartObject(); 
writer.WritePropertyName("BlahProp"); 
serializer.Serialize(writer, "blah blah blah"); 
writer.WriteEndObject(); 

來看,這又和這裏的輸出:(注意英文別名如何爲對象現在不是字符串)

{ 
    "Id": 43, 
    "AccountName": "John", 
    "DefaultAssignToId": 43, 
    "OtherId": 19, 
    "OtherName": { 
    "BlahProp": "blah blah blah" 
    } 
} 

該角色集合還需要一個轉換器。首先需要注意兩點,您不需要指定屬性名稱,其次,Role類裝飾的任何Json屬性將按預期工作。

using BaseCollection = System.Collections.Generic.ICollection<JsonContractandConvert.Models.Role>; 
public class RemoveAccountsFromRolesConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(BaseCollection).IsAssignableFrom(objectType); 
    } 

    public override bool CanRead 
    { 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     if (!this.CanConvert(value.GetType())) return; 

     var entities = value as BaseCollection; 
     if (entities == null) return; 

     writer.WriteStartArray(); 
     foreach (var entity in entities) 
     { 
     entity.Accounts = null; 
     serializer.Serialize(writer, entity); 
     } 
     writer.WriteEndArray(); 
    } 
} 

最後,這裏有一對夫婦我爲我的真正的項目創建轉換器:

這一個對象轉換爲淺拷貝。如果您有一個集合,其中包含該集合包含的集合可能導致自引用循環,那麼這非常有用。你爲什麼不使用簡單的`JsonIgnore`

public class DataSnapInConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(DataSnapIn) == (objectType); 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (this.CanConvert(objectType) == false) return null; 

     var jo = JObject.Load(reader); 
     var typeName = jo["snapInType"] ?? jo["SnapInType"]; //the abstract classes have this property to identify what concrete class they are. 
     var typeNameString = typeName.ToString(); 

     var deserializeType = Type.GetType(typeNameString); 
     if(deserializeType == null) 
      throw new Exception("SnapInType is null or does not reference a valid class."); 

     var result = Activator.CreateInstance(deserializeType); 
     serializer.Populate(jo.CreateReader(), result); 

     return result; 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
}