2016-08-04 5 views
0

Gibt es eine Möglichkeit, ich kann meinen eigenen benutzerdefinierten JsonConverter, der die Daten vor dem Schreiben der JSON ändert, die mit einer verschachtelten Eltern-Kind-Struktur funktioniert? Wann immer ich im Moment versuche, habe ich einen Fehler, der auf sich selbst bezieht. Ich habe versucht, nach Lösungen zu suchen, aber ich kann nichts finden, was passt.Benutzerdefinierte JsonConverter selbstreferenzierende Schleife

Ich habe eine einfache Lösung erstellt, um das Problem zu demonstrieren.

Ich habe das folgende Modell und Konverter

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); } 
} 

und der Testcode zu verwenden, um es

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); 

jedoch, wenn es die Json zu schreiben versucht, gibt es eine Schleife Fehler Selbstreferenzierung, vermutlich wenn es versucht, die Liste der Kinder zu verarbeiten. Ich kann diesen Fehler verhindern, indem ich ein JObject für die Konvertierung verwende, aber das verhindert, dass mein benutzerdefinierter Konverter für die untergeordneten Elemente verwendet wird. Ich möchte meine benutzerdefinierte WriteJson-Methode für jede Ebene dieser Struktur auslösen, damit ich einige Daten ändern kann, bevor ich sie schreibe.

Gibt es eine Möglichkeit, dies zu tun und den Fehler der selbstreferenzierenden Schleife zu umgehen?

+0

Es gibt einige Optionen auf [JSON.Net wirft StackOverflowException bei Verwendung von '[JsonConvert]'] (https://stackoverflow.com/questions/29719509/json-net-throws-stackoverflowex ception-while-using-jsonconvert). – dbc

+0

Auch verwandt: [Json.NET andere JSON-Struktur, basierend auf Enum-Wert] (https://StackOverflow.com/Questions/37896661/JSON-NET-Different-Json-Structure-Based-on-enum-Value?noredirect= 1 & lq = 1). – dbc

Antwort

1

Nach etwas mehr Arbeit, fand ich einen Weg, es zu tun, also dachte ich, ich würde es für jeden anderen veröffentlichen, der ein ähnliches Problem hat. Ich musste sicherstellen, dass ich beim Serialisieren des NestedModel jede Eigenschaft per Reflektion einzeln serialisierte (dann, wenn diese Eigenschaft eine Liste von NestedModel war, rief sie meinen Serializer für jeden wieder auf).

Hier ist das Modell/custom-Wandler

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); 
    } 
} 

und der Testcode

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); 

die resultierende Json erstellt dann

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