2015-12-09 1 views
10

Ich habe ein Newtonsoft JSON.NET JsonConverter, um die Deserialisierung einer Eigenschaft zu unterstützen, deren Typ eine abstrakte Klasse ist. Der Kern sieht es wie folgt aus:Behandeln von Nullobjekten in der ReadJson-Methode des benutzerdefinierten JsonConverters

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject jsonObject = JObject.Load(reader); 

     if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer); 
     if (jsonObject["StopPhrase"] != null) return jsonObject.ToObject<Parrot>(serializer); 

     return null; 
    } 

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

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

Hier sind die Klassen, die es Griffe:

public abstract class Animal 
{ } 

public class Cat : Animal 
{ 
    public int Lives { get; set; } 
} 

public class Parrot : Animal 
{ 
    public string StopPhrase { get; set; } 
} 

public class Person 
{ 
    [JsonConverter(typeof(PetConverter))] 
    public Animal Pet { get; set; } 
} 

Dies funktioniert gut, wenn ein Person Deserialisieren, die ein Nicht-Null-Pet hat. Aber wenn die Pet null ist, dann werden die ReadJson Methode bricht in der ersten Zeile mit diesem ein JsonReaderException:

Eine Ausnahme vom Typ ‚Newtonsoft.Json.JsonReaderException‘ in Newtonsoft.Json.dll aufgetreten war aber nicht behandelt in Benutzercode

Weitere Informationen: Fehler beim Lesen von JObject von JsonReader. Das aktuelle JsonReader-Objekt ist kein Objekt: Null. Pfad 'Pet', Zeile 1, Position 11.

Ich habe the Custom JsonConverter Dokumentation überprüft, aber es ist nur über ein Schreiben Konverter. Ich habe folgendes versucht:

if (reader.Value == null) return null; // this inverts the [Test] results 

Aber dann bekomme ich:

JsonSerializationException: Zusätzlicher Text in JSON-String gefunden, nachdem Deserialisieren Objekt beenden.

Für Fälle, in denen die Eigenschaft aufgefüllt ist.

Kurz gesagt, was ist der richtige Weg, um mit dieser Situation umzugehen?


Für Vollständigkeit, hier sind einige Unit-Tests, die die Frage auf der Hand zeigen:

[TestFixture] 
public class JsonConverterTests 
{ 
    [Test] 
    public void Cat_survives_serialization_roundtrip() 
    { 
     var person = new Person { Pet = new Cat { Lives = 9 } }; 
     var serialized = JsonConvert.SerializeObject(person); 
     var deserialized = JsonConvert.DeserializeObject<Person>(serialized); 
     Assert.That(deserialized.Pet, Is.InstanceOf<Cat>()); 
     Assert.That((deserialized.Pet as Cat).Lives, Is.EqualTo(9)); 
    } 

    [Test] 
    public void Parrot_survives_serialization_roundtrip() 
    { 
     var person = new Person { Pet = new Parrot { StopPhrase = "Lorrie!" } }; 
     var serialized = JsonConvert.SerializeObject(person); 
     var deserialized = JsonConvert.DeserializeObject<Person>(serialized); 
     Assert.That(deserialized.Pet, Is.InstanceOf<Parrot>()); 
     Assert.That((deserialized.Pet as Parrot).StopPhrase, Is.EqualTo("Lorrie!")); 
    } 

    [Test] 
    public void Null_property_does_not_break_converter() 
    { 
     var person = new Person { Pet = null }; 
     var serialized = JsonConvert.SerializeObject(person); 
     var deserialized = JsonConvert.DeserializeObject<Person>(serialized); 
     Assert.That(deserialized.Pet, Is.Null); 
    } 
} 

Antwort

18

Während die Frage schreiben, speziell beim Schreiben der „was habe ich versucht,“ etwas, ich eine mögliche finden Lösung:

if (reader.TokenType == JsonToken.Null) return null; 

ich poste dies aus zwei Gründen:

  1. Wenn es gut genug ist, könnte es jemand anderem mit der gleichen Frage helfen.
  2. Ich könnte von einer besseren, konkurrierenden Lösung von der Antwort eines anderen lernen.

FWIW, hier ist die vollständige JsonConverter für sehr einfache Handhabung der Deserialisierung einer Immobilie, deren Typ ist eine abstrakte Klasse:

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

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

     JObject jsonObject = JObject.Load(reader); 

     if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer); 
     if (jsonObject["StopPhrase"] != null) return jsonObject.ToObject<Parrot>(serializer); 

     return null; 
    } 

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

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

Und/oder Sie können Ihre JSON als 'JToken' laden und prüfen, ob' token.Type == JTokenType.Null'. Obwohl Ihre Lösung am besten scheint. – dbc

+1

In meinem Fall hatte der 'Leser' nicht den korrekten Status, wenn er null zurückgibt.Daher habe ich eine Kombination aus der Antwortlösung und dem Vorschlag von [dbc]: if (reader.TokenType == JsonToken.Null) {JToken.Load (reader); null zurückgeben; } '. – JanDotNet