2016-06-18 29 views
3

我需要將我的類轉換爲JSON,並使用Json.NET。但是,我可以有不同的JSON結構,比如:Json.NET不同的JSON結構,基於枚舉值

{ 
    name: "Name", 
    type: "simple1", 
    value: 100 
}; 

{ 
    name: "Name", 
    type: { 
     optional1: { 
      setting1: "s1", 
      setting2: "s2", 
      ///etc. 
    }, 
    value: 100 
}; 

我的C#代碼是:

public class Configuration 
{ 
    [JsonProperty(PropertyName = "name")] 
    public string Name{ get; set; } 

    [JsonProperty(PropertyName = "type")] 
    public MyEnumTypes Type { get; set; } 

    public OptionalType TypeAdditionalData { get; set; } 

    [JsonProperty(PropertyName = "value")] 
    public int Value { get; set; } 
    public bool ShouldSerializeType() 
    { 
     OptionalSettingsAttribute optionalSettingsAttr = this.Type.GetAttributeOfType<OptionalSettingsAttribute>(); 
     return optionalSettingsAttr == null; 
    } 

    public bool ShouldSerializeTypeAdditionalData() 
    { 
     OptionalSettingsAttribute optionalSettingsAttr = this.Type.GetAttributeOfType<OptionalSettingsAttribute>(); 
     return optionalSettingsAttr != null; 
    } 
} 

public enum MyEnumTypes 
{ 
    [EnumMember(Value = "simple1")] 
    Simple1, 

    [EnumMember(Value = "simple2")] 
    Simple2, 

    [OptionalSettingsAttribute] 
    [EnumMember(Value = "optional1")] 
    Optional1, 

    [EnumMember(Value = "optional2")] 
    [OptionalSettingsAttribute] 
    Optional2 
} 

我的想法是,當Configuration.Type - 值沒有屬性OptionalSettingsAttribute - 將其序列化爲type: "simple1"。否則 - 使用Configuration.Type - 值作爲類型的值鍵(type: { optional1: {} }),值Configuration.TypeAdditionalDataoptional1 - 值(如上面的2個簡單的JSON)。

我試圖創建一個自定義的轉換器,如:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     return serializer.Deserialize<Configuration>(reader); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     //my changes here 

     serializer.Serialize(writer, value); 
    } 

但是當我添加[JsonConverter(typeof(ConfigurationCustomConverter))]屬性Configuration類:

[JsonConverter(typeof(ConfigurationCustomConverter))] 
public class Configuration 

,並呼籲JsonConvert.SerializeObject(configurationObj);,我收到一個錯誤:

Self referencing loop detected with type 'Configuration'. Path ''.

你有什麼想法如何改變我的代碼將我的類序列化爲2個不同的JSON結構? 注意:我不會使用相同的類來反序列化JSON。

謝謝!

回答

2

你所得到的Self referencing loop detected異常的原因是,你的轉換器的WriteJson方法遞歸調用自身。當您將轉換器應用於使用[JsonConverter(typeof(ConfigurationCustomConverter))]的類型時,WriteJson()方法將無條件地替換爲 Json.NET的默認實現。因此,你的內心呼籲:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    //my changes here 
    serializer.Serialize(writer, value); 
} 

會導致堆棧溢出。 Json.NET注意到這一點,而是拋出你看到的異常。有關更多詳細信息,請參見JSON.Net throws StackOverflowException when using [JsonConvert()]。設置ReferenceLoopHandling.Ignore只會導致無限遞歸被跳過,從而使對象爲空。

您有幾種選擇來解決這個問題:

  1. 您可以手動填寫了超過TypeTypeAdditionalData其他然後寫出來的自定義"type"財產最後所有屬性名稱和值。例如:

    [JsonConverter(typeof(ConfigurationConverter))] 
    public class Configuration 
    { 
        [JsonProperty(PropertyName = "name")] 
        public string Name { get; set; } 
    
        public MyEnumTypes Type { get; set; } 
    
        public OptionalType TypeAdditionalData { get; set; } 
    
        [JsonProperty(PropertyName = "value")] 
        public int Value { get; set; } 
    } 
    
    class ConfigurationConverter : JsonConverter 
    { 
        const string typeName = "type"; 
    
        public override bool CanConvert(Type objectType) 
        { 
         return typeof(Configuration).IsAssignableFrom(objectType); 
        } 
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
        { 
         if (reader.TokenType == JsonToken.Null) 
          return null; 
         var config = (existingValue as Configuration ?? (Configuration)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator()); 
    
         // Populate the regular property values. 
         var obj = JObject.Load(reader); 
         var type = obj.RemoveProperty(typeName); 
         using (var subReader = obj.CreateReader()) 
          serializer.Populate(subReader, config); 
    
         // Populate Type and OptionalType 
         if (type is JValue) // Primitive value 
         { 
          config.Type = type.ToObject<MyEnumTypes>(serializer); 
         } 
         else 
         { 
          var dictionary = type.ToObject<Dictionary<MyEnumTypes, OptionalType>>(serializer); 
          if (dictionary.Count > 0) 
          { 
           config.Type = dictionary.Keys.First(); 
           config.TypeAdditionalData = dictionary.Values.First(); 
          } 
         } 
    
         return config; 
        } 
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
        { 
         var config = (Configuration)value; 
         var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(config.GetType()); 
         writer.WriteStartObject(); 
         foreach (var property in contract.Properties 
          .Where(p => p.Writable && (p.ShouldSerialize == null || p.ShouldSerialize(config)) && !p.Ignored)) 
         { 
          if (property.UnderlyingName == "Type" || property.UnderlyingName == "TypeAdditionalData") 
           continue; 
          var propertyValue = property.ValueProvider.GetValue(config); 
          if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore) 
           continue; 
          writer.WritePropertyName(property.PropertyName); 
          serializer.Serialize(writer, propertyValue); 
         } 
         writer.WritePropertyName(typeName); 
         if (config.Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() == null) 
         { 
          serializer.Serialize(writer, config.Type); 
         } 
         else 
         { 
          var dictionary = new Dictionary<MyEnumTypes, OptionalType> 
          { 
           { config.Type, config.TypeAdditionalData }, 
          }; 
          serializer.Serialize(writer, dictionary); 
         } 
         writer.WriteEndObject(); 
        } 
    } 
    
    public class OptionalType 
    { 
        public string setting1 { get; set; } 
    } 
    
    public class OptionalSettingsAttribute : System.Attribute 
    { 
        public OptionalSettingsAttribute() 
        { 
        } 
    } 
    
    [JsonConverter(typeof(StringEnumConverter))] 
    public enum MyEnumTypes 
    { 
        [EnumMember(Value = "simple1")] 
        Simple1, 
    
        [EnumMember(Value = "simple2")] 
        Simple2, 
    
        [OptionalSettingsAttribute] 
        [EnumMember(Value = "optional1")] 
        Optional1, 
    
        [EnumMember(Value = "optional2")] 
        [OptionalSettingsAttribute] 
        Optional2 
    } 
    
    public static class EnumExtensions 
    { 
        public static TAttribute GetCustomAttributeOfEnum<TAttribute>(this Enum value) 
         where TAttribute : System.Attribute 
        { 
         var type = value.GetType(); 
         var memInfo = type.GetMember(value.ToString()); 
         return memInfo[0].GetCustomAttribute<TAttribute>(); 
        } 
    } 
    
    public static class JsonExtensions 
    { 
        public static JToken RemoveProperty(this JObject obj, string name) 
        { 
         if (obj == null) 
          return null; 
         var property = obj.Property(name); 
         if (property == null) 
          return null; 
         var value = property.Value; 
         property.Remove(); 
         property.Value = null; 
         return value; 
        } 
    } 
    

    通知我說[JsonConverter(typeof(StringEnumConverter))]到您的枚舉。這可確保類型始終以字符串形式書寫。

    樣品fiddle

  2. 您可以通過JSON.Net throws StackOverflowException when using [JsonConvert()]中顯示的技術禁用對轉換器的遞歸調用,生成默認序列化,根據需要對其進行修改並寫出。

  3. 你可能避免使用轉換器完全由標記TypeTypeAdditionalData[JsonIgnore]和引入更多的私人財產進行序列化和反序列化"type"

    public class Configuration 
    { 
        [JsonProperty(PropertyName = "name")] 
        public string Name { get; set; } 
    
        [JsonIgnore] 
        public MyEnumTypes Type { get; set; } 
    
        [JsonIgnore] 
        public OptionalType TypeAdditionalData { get; set; } 
    
        [JsonProperty("type")] 
        JToken SerializedType 
        { 
         get 
         { 
          if (Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() == null) 
          { 
           return JToken.FromObject(Type); 
          } 
          else 
          { 
           var dictionary = new Dictionary<MyEnumTypes, OptionalType> 
           { 
            { Type, TypeAdditionalData }, 
           }; 
           return JToken.FromObject(dictionary); 
          } 
         } 
         set 
         { 
          if (value == null || value.Type == JTokenType.Null) 
          { 
           TypeAdditionalData = null; 
           Type = default(MyEnumTypes); 
          } 
          else if (value is JValue) 
          { 
           Type = value.ToObject<MyEnumTypes>(); 
          } 
          else 
          { 
           var dictionary = value.ToObject<Dictionary<MyEnumTypes, OptionalType>>(); 
           if (dictionary.Count > 0) 
           { 
            Type = dictionary.Keys.First(); 
            TypeAdditionalData = dictionary.Values.First(); 
           } 
          } 
         } 
        } 
    
        [JsonProperty(PropertyName = "value")] 
        public int Value { get; set; } 
    } 
    
+0

非常感謝@dbc!我嘗試瞭解決方案#3,它爲我工作! – Pepo

1

如果您需要移過該錯誤,則可以配置序列化以忽略參考循環。這是通過使用SerializaObject()重載之一來完成的。

JsonConvert.SerializeObject(configurationObj, 
        new JsonSerializerSettings() 
        { 
         ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
        }); 
+0

謝謝!這固定了「Reference loop」錯誤,但是如果'WriteJson'方法有下一個代碼: 'public override void WriteJson(JsonWriter writer,object value,JsonSerializer serializer) { serializer.Serialize(writer,value); }' 序列化的字符串是空的 - 沒有任何轉換。這是對的嗎?我如何在我的自定義轉換器中使用默認轉換器? 謝謝! – Pepo

+0

我沒有遇到你所描述的情況,我不在我的計算機旁邊調查更多。我將在星期一。希望其他人能夠在此之前提供幫助。請用您提供的信息更新您的問題,並提供相關信息,以便下一個人可以幫助回答完整問題。 –

+1

謝謝!我在@dbc中使用瞭解決方案#3 - 答案,它適用於我! – Pepo