2016-06-18 3 views
3

私のクラスをJSONに変換する必要があり、Json.NETを使用します。しかし、私のようなさまざまなJSON構造を持つことができます。Json.NETのenson値に基づく異なるjson構造

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

または

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

私のC#コードは次のとおりです。Configuration.Typeとき

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 
} 

私の考えだった - 値がOptionalSettingsAttribute属性いません - type: "simple1"としてシリアル化します。そうでない場合 - Configuration.Type - 型の値のキー(type: { optional1: {} })の値とConfiguration.TypeAdditionalDataの値をoptional1 - 値(上記の2つの単純なJSONのような値)として使用します。

私は次のように、カスタムコンバータを作成しようとしました:私はConfigurationクラスに[JsonConverter(typeof(ConfigurationCustomConverter))]属性を追加するとき

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))] 
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を設定すると、単純に無限再帰がスキップされ、オブジェクトは空のままになります。

あなたはこの問題を解決するためのいくつかのオプションがあります:あなたは、手動で最後のカスタム"type"プロパティを書き出しTypeTypeAdditionalData以外のすべてのプロパティ名と値を書くことができ

  1. を。たとえば:

    [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. あなたは[JsonIgnore]としてTypeTypeAdditionalDataをマーキングして"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()の過負荷の1つを使用して行われます。

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

ありがとうございました!これは "参照ループ"エラーを修正しましたが、 'WriteJson'メソッドに次のコードがあるとします: ' public override void WriteJson(JsonWriterライター、オブジェクト値、JsonSerializerシリアライザ) { serializer.Serialize(writer、value); } ' シリアライズされた文字列は空です。何も変換されていません。それが正しいか?カスタムコンバータでデフォルトコンバータをどのように使用できますか? ありがとうございます! – Pepo

+0

私は、あなたが記述している状況にぶち込まれておらず、私はコンピュータの隣にいてもっと調査するつもりはありません。私は月曜日になるでしょう。その前に他の人が助けてくれることを願っています。次の人が完全な質問に答えるために、私が提供した情報を使って行った進捗状況で質問を更新してください。 –

+1

ありがとうございます!私は@データベースの答え#3を使用しました - それは私のために働いています! – Pepo

関連する問題