Подтвердить что ты не робот

Обработка нулевых объектов в пользовательском методе JsonConverter ReadJson

У меня есть Newtonsoft JSON.NET JsonConverter, чтобы помочь десериализовать свойство, тип которого является абстрактным классом. Суть его выглядит так:

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

Вот классы, которые он обрабатывает:

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

Это отлично работает при десериализации a Person, у которого есть ненулевой Pet. Но если значение Pet равно нулю, то метод ReadJson разбивается на первую строку с этим a JsonReaderException:

Исключение типа "Newtonsoft.Json.JsonReaderException" произошло в Newtonsoft.Json.dll, но не было обработано в коде пользователя

Дополнительная информация: Ошибка чтения JObject из JsonReader. Текущий элемент JsonReader не является объектом: Null. Путь 'Pet', строка 1, позиция 11.

Я проверил пользовательскую JsonConverter, но это всего лишь конвертер для записи. Я пробовал следующее:

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

Но потом я получаю:

JsonSerializationException: дополнительный текст, найденный в строке JSON после завершения десериализации объекта.

В случаях, когда свойство заполнено.

Короче говоря, каков правильный способ справиться с этой ситуацией?


Для полноты, вот некоторые модульные тесты, которые демонстрируют проблему:

[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);
    }
}
4b9b3361

Ответ 1

При написании вопроса, особенно при написании бит "что я пробовал", я нахожу одно возможное решение:

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

Я отправляю это по двум причинам:

  • Если это будет достаточно хорошо, это может помочь кому-то другому с тем же вопросом.
  • Я мог бы узнать о лучшем конкурирующем решении от кого-то другого.

FWIW, здесь полный JsonConverter для очень простой десериализации дескрипции дескрипции свойства, тип которого является абстрактным классом:

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