2016-08-04 8 views
0

は、私は、ネストされた親子構造で動作するJSONを書き出す前に、データを変更する自分のカスタムJsonConverterを、作成することができます方法はありますか?私が現時点で試してみると、私は自己参照ループエラーに終わる。私は解決策を探してみましたが、一致するものは全く見つかりません。カスタムJsonConverter自己参照ループ

問題を示すための簡単なソリューションを作成しました。

私はそれがJSONを書き込もうとすると

string sourceJson = @"{ 
    ""Id"": 1, 
    ""Forename"": ""John"", 
    ""Surname"": ""Smith"", 
    ""Children"": 
    [ 
     { 
      ""Id"": 2, 
      ""Forename"": ""Joe"", 
      ""Surname"": ""Bloggs"", 
      ""Children"": null 
     } 
    ] 
}"; 

var settings = new JsonSerializerSettings() 
{ 
    Converters = new List<JsonConverter>() 
    { 
     new NestedModelJsonConverter() 
    }, 
    Formatting = Formatting.Indented 
}; 
var nestedModel = JsonConvert.DeserializeObject<NestedModel>(sourceJson, settings); 

string outputJson = JsonConvert.SerializeObject(nestedModel, settings); 

それを使用するには、次のモデルとコンバータ

public class NestedModel 
{ 
    public int Id { get; set; } 

    public string Forename { get; set; } 

    public string Surname { get; set; } 

    public string Custom { get; set; } 


    public List<SimpleModel> Children { get; set; } 
} 

public class NestedModelJsonConverter : JsonConverter 
{ 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var nestedModel = value as NestedModel; 
     nestedModel.Custom = "Modified by Json Converter"; 

     //This causes a self referencing loop error 
     serializer.Serialize(writer, value); 

     //This resolves the self referencing loop error, but it does not call my custom Json Converter for any of the Children, and instead uses the default serialization 
     //var jo = JObject.FromObject(value); 
     //jo.WriteTo(writer); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 

     // Load JObject from stream 
     JObject jObject = JObject.Load(reader); 

     // Create target object based on JObject 
     NestedModel target = new NestedModel(); 

     // Populate the object properties 
     StringWriter writer = new StringWriter(); 
     serializer.Serialize(writer, jObject); 
     using (JsonTextReader newReader = new JsonTextReader(new StringReader(writer.ToString()))) 
     { 
      newReader.Culture = reader.Culture; 
      newReader.DateParseHandling = reader.DateParseHandling; 
      newReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; 
      newReader.FloatParseHandling = reader.FloatParseHandling; 
      serializer.Populate(newReader, target); 
     } 

     return target; 
    } 

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

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

    public override bool CanConvert(Type objectType) { return typeof(NestedModel).IsAssignableFrom(objectType); } 
} 

とテストコードは、しかし、それはおそらく、自己参照ループ誤差を与えてい子どものリストを処理しようとするとき 変換でJObjectを使用することでそのエラーを防ぐことができますが、これにより、カスタムコンバータが子要素に使用されなくなります。私は自分のカスタムWriteJsonメソッドをその構造の各レベルに対して起動して、書き込み前にいくつかのデータを変更できるようにしたいと思います。

これを実行し、自己参照ループエラーを回避する方法はありますか?

+0

[JSON.Netスローでいくつかのオプションがありますされます'[JsonConvert]'を使用した場合のStackOverflowException(https://stackoverflow.com/questions/29719509/json-net-throws-stackoverflowex) jsonconvertを使用している場合)。 – dbc

+0

関連項目:[enum値に基づくJson.NETの異なるjson構造](https://stackoverflow.com/questions/37896661/json-net-different-json-structure-based-on-enum-value?noredirect= 1&lq = 1)。 – dbc

答えて

1

もう少し仕事をした後、私はそれを行う方法を見つけたので、同様の問題を抱えている誰かのために投稿すると思いました。 NestedModelのシリアライズ時に、リフレクションを使用して各プロパティを個別にシリアル化していなければなりませんでした(そのプロパティがNestedModelのリストであった場合、それぞれに対してシリアライザが呼び出されました)。ここで

は、モデル/カスタムコンバータ

public class NestedModel 
{ 
    public int Id { get; set; } 

    public string Forename { get; set; } 

    public string Surname { get; set; } 

    public string Custom { get; set; } 


    public List<NestedModel> Children { get; set; } 
} 

public class NestedModelJsonConverter : JsonConverter 
{ 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var nestedModel = value as NestedModel; 
     nestedModel.Custom = "Modified by Json Converter"; 

     JObject jo = new JObject(); 
     Type type = nestedModel.GetType(); 

     foreach (PropertyInfo prop in type.GetProperties()) 
     { 
      if (prop.CanRead) 
      { 
       object propVal = prop.GetValue(nestedModel, null); 
       if (propVal != null) 
       { 
        jo.Add(prop.Name, JToken.FromObject(propVal, serializer)); 
       } 
      } 
     } 
     jo.WriteTo(writer); 
    } 


    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 

     // Load JObject from stream 
     JObject jObject = JObject.Load(reader); 

     // Create target object based on JObject 
     NestedModel target = new NestedModel(); 


     // Populate the object properties 
     StringWriter writer = new StringWriter(); 
     serializer.Serialize(writer, jObject); 
     using (JsonTextReader newReader = new JsonTextReader(new StringReader(writer.ToString()))) 
     { 
      newReader.Culture = reader.Culture; 
      newReader.DateParseHandling = reader.DateParseHandling; 
      newReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; 
      newReader.FloatParseHandling = reader.FloatParseHandling; 
      serializer.Populate(newReader, target); 
     } 

     return target; 
    } 

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

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

    public override bool CanConvert(Type objectType) 
    { 
     return typeof(NestedModel).IsAssignableFrom(objectType); 
    } 
} 

とテストコード

string sourceJson = @"{ 
    ""Id"": 1, 
    ""Forename"": ""John"", 
    ""Surname"": ""Smith"", 
    ""Children"": 
    [ 
     { 
      ""Id"": 2, 
      ""Forename"": ""Joe"", 
      ""Surname"": ""Bloggs"", 
      ""Children"": null 
     } 
    ] 
}"; 

var settings = new JsonSerializerSettings() 
{ 
    Converters = new List<JsonConverter>() 
    { 
     new NestedModelJsonConverter() 
    }, 
    Formatting = Formatting.Indented, 
}; 
var nestedModel = JsonConvert.DeserializeObject<NestedModel>(sourceJson, settings); 

string outputJson = JsonConvert.SerializeObject(nestedModel, settings); 

は、作成した結果のJSONだ後、

{ 
    "Id": 1, 
    "Forename": "John", 
    "Surname": "Smith", 
    "Custom": "Modified by Json Converter", 
    "Children": [ 
    { 
     "Id": 2, 
     "Forename": "Joe", 
     "Surname": "Bloggs", 
     "Custom": "Modified by Json Converter" 
    } 
    ] 
}