Json.NET не десериализует свойство с пользовательским геттером и неизменным типом - PullRequest
1 голос
/ 17 октября 2019

Я использую Newtonsoft.Json (он же Json.Net) для сериализации моего класса:

public class ClassWithCustomProperty
{
    private ImmutableValue value;
    public ImmutableValue Value
    {
        get => value = (value ?? new ImmutableValue(0));
        set => this.value = value;
    }
}

public class ImmutableValue
{
    public readonly int Value;
    public ImmutableValue(int value) => Value = value;
}

Но когда я сериализую и десериализую объект, я не получаю ожидаемое значение:

public static class Program
{
    public static void Main()
    {
        var json = JsonConvert.SerializeObject(new ClassWithCustomProperty
        {
            Value = new ImmutableValue(42)
        });
        // JSON is {"Value": {"Value": 42}} as expected

        var obj = JsonConvert.DeserializeObject<ClassWithCustomProperty>(json);
        Console.WriteLine(obj.Value?.Value); 
        // But after deserialization obj.Value.Value is 0, instead of 42
    }
}

Что не так или как изменить это поведение?

Я заметил, что если я сериализую и десериализую ImmutableValue сам по себе, он прекрасно работает:

var json = JsonConvert.SerializeObject(new ImmutableValue(42));
var obj = JsonConvert.DeserializeObject<ImmutableValue>(json);
Console.WriteLine(obj.Value);  // 42

1 Ответ

3 голосов
/ 17 октября 2019

Виновной является эта строка в вашем ClassWithCustomProperty классе:

    get => value = (value ?? new ImmutableValue(0));

Поведение Json.Net по умолчанию состоит в том, чтобы повторно использовать существующие объекты, а не заменять их. Поэтому, когда пришло время десериализовать ImmutableValue на ClassWithCustomProperty, сериализатор сначала проверяет, существует ли существующее значение. Метод доступа (см. Выше) обнаруживает, что его нет, поэтому он создает значение со значением 0, которое возвращается сериализатору. Затем сериализатор пытается повторно использовать этот существующий объект, но, поскольку он доступен только для чтения, невозможно установить для него значение 42. Таким образом, значение остается нулевым.

Существует настройка ObjectCreationHandling, которую можно использовать для изменения этого поведения. Если вы установите его на Replace, он будет работать так, как вы хотите. Попробуйте так:

var settings = new JsonSerializerSettings 
{ 
    ObjectCreationHandling = ObjectCreationHandling.Replace 
};
var obj = JsonConvert.DeserializeObject<ClassWithCustomProperty>(json, settings);

Скрипка: https://dotnetfiddle.net/UVDMsL

...