JsonConvert не инициализирует свойства при десериализации, если setter не является универсальным - PullRequest
2 голосов
/ 09 марта 2020

Почему JsonConvert не может инициализировать значения свойств, когда методы getter / setter не являются общими / значениями по умолчанию.
Например, JsonConvert десериализует следующий класс (или, скорее, свойство "Value"), как и должно быть. Скажем, мой Json файл содержит Value = 5, тогда мой десериализованный объект SomeObjectX будет иметь свое свойство "Value" равным 5:

public class SomeClass
{
    public int Value { get; set; }
}

Однако, если я хочу, чтобы установщик был немного более сложным и сделай что-то особенное, тогда это не сработает. Вот новый класс:

public class SomeClass
{
    public int MinValue { get; set; }
    public int MaxValue { get; set; }

    private int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            // Restrict range to Min/Max
            if (MaxValue < value)
                _value = MaxValue;
            else if (MinValue > value)
                _value = MinValue;
            else
                _value = value;
        }
    }
}

Для информации, вот как я вызываю JsonConvert для десериализации в объекты:

SomeClass SomeObjectX = JsonConvert.DeserializeObject<SomeClass>(File.ReadAllText(@"e:\someObject.json"), settings);

Есть ли способ заставить это работать? Другими словами, если мой Json содержит свойство инициализации «Значение», равное 10, то при десериализации я должен установить значение «Значение» на 10.

Редактировать

Сериализованный Json файл выглядит следующим образом:

{
  "MaxValue": 10,
  "MinValue": 0,
  "Value": 5
}

Вот неинициализированный объект, который я получаю во время выполнения (обратите внимание, что «Значение» равно 0, а не 5, как и должно быть):
enter image description here

Вот тест на то, как я создал свой экземпляр объекта, инициализировал его, сохранил его в Json и затем десериализовал его обратно в объект:

// Create and init object
var param = new SomeClass(); 
param.MaxValue = 10;
param.Value = 5;

// Settings - Not making any difference with or without
JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    ObjectCreationHandling = ObjectCreationHandling.Replace
};

// Serialise into Json
File.WriteAllText(@"e:\param.json", JsonConvert.SerializeObject(param, settings));

// Deserialise back into object
SomeClass obj = JsonConvert.DeserializeObject<SomeClass>(File.ReadAllText(@"e:\param.json"), settings);

Ответы [ 2 ]

4 голосов
/ 09 марта 2020

Проблема, с которой вы сталкиваетесь, связана с упорядочением десериализации свойств. Ваша исходная модель содержит временную связь между свойствами Value и MaxValue, и в зависимости от порядка установки значений вы получите разные результаты для вашего объекта.

Вы должны быть Можно управлять порядком десериализации с помощью атрибута [Order] в свойствах, но я бы рекомендовал вместо этого сделать вашу модель неизменной и передать все 3 значения через конструктор. Таким образом, вы также полностью избегаете временную связь, что приводит к лучшему дизайну.

0 голосов
/ 09 марта 2020

Ответ на мой вопрос с использованием ввода julealgon.
Установите атрибут «JsonProperty Order», как показано в следующем примере («Value» должен использовать самый высокий номер заказа):

using Newtonsoft.Json;

public class SomeClass
{
    [JsonProperty(Order = 1)]
    public int MinValue { get; set; }

    [JsonProperty(Order = 2)]
    public int MaxValue { get; set; }

    private int _value;

    [JsonProperty(Order = 3)]
    public int Value
    {
        get { return _value; }
        set
        {
            // Restrict range to Min/Max
            if (MaxValue < value)
                _value = MaxValue;
            else if (MinValue > value)
                _value = MinValue;
            else
                _value = value;
        }
    }
}

Таким образом, JsonConvert сначала десериализуется MinValue и MaxValue до десериализации Value.

Или с использованием неизменяемых свойств:

public class SomeClass
{
    // Constructor
    public SomeClass(int, value, int minValue, int maxValue)
    {
        MinValue = minValue;
        MaxValue = maxValue;
        // Set value after Min/MaxValue
        Value = value;
    }
    public int MinValue { get; }
    public int MaxValue { get; }

    private int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            // Restrict range to Min/Max
            if (MaxValue < value)
                _value = MaxValue;
            else if (MinValue > value)
                _value = MinValue;
            else
                _value = value;
        }
    }
}
...