Почему System.Version в строке JSON неправильно десериализуется?
Контекст: мне нужно передать объект, содержащий большое количество свойств / полей (в UI Layer из Middle Tier Layer). Среди этого списка свойств одно имеет тип Version, который неправильно десериализовывается из строкового формата JSON. Я выбрал формат JSON поверх XML, поскольку сериализация JSON в строку вернет короткий результат строки.
Проблема: System.Version не десериализован правильно. Я пробовал две разные библиотеки.NET. Ниже приведены фрагменты кода для каждого:
Фрагмент кода 1 с использованием библиотеки ServiceStack .NET:
var version = new Version(1, 2, 3, 0);
string reportJSON = JsonSerializer.SerializeToString<Version>(version);
//{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0}
Version report2 = JsonSerializer.DeserializeFromString<Version>(reportJSON);
string reportJSON2 = JsonSerializer.SerializeToString<Version>(report2);
//{"Major":0,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}
Код фрагмента 2 с использованием библиотеки Newtonsoft .NET, но с тем же результатом:
var version = new Version(1, 2, 3, 0);
string reportJSON = JsonConvert.SerializeObject(version);
//{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0}
Version report2 = JsonConvert.DeserializeObject<Version>(reportJSON);
string reportJSON2 = JsonConvert.SerializeObject(report2);
//{"Major":0,"Minor":0,"Build":-1,"Revision":-1,"MajorRevision":-1,"MinorRevision":-1}
Как это исправить? Или какая другая библиотека JSON.NET может работать правильно?
2 ответа
Newtonsoft.Json
Библиотека предоставляет набор общих преобразователей в Newtonsoft.Json.Converters
пространство имен, включая VersionConverter
Вы можете использовать для сериализации и десериализацииSystem.Version
,
Обратите внимание, что вы должны использоватьVersionConverter
как для сериализации, так и для десериализации.
Это потому, что стандартная сериализация будет генерировать, например:{"Major":1,"Minor":2,"Build":3,"Revision":0,"MajorRevision":0,"MinorRevision":0}
в то время как VersionConverter
десериализация ожидает простую строку, как в "1.2.3"
,
Таким образом, использование будет:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
string s = JsonConvert.SerializeObject(version, new VersionConverter());
Version v = JsonConvert.DeserializeObject<Version>(s, new VersionConverter());
Я не уверен, что это первая версия Newtonsoft.Json
это включает в себя этот конвертер. У меня есть, и это 5.0.6.
Свойства Version
У класса нет сеттера. Они просто возвращают значение своих соответствующих приватных полей. Следовательно, десериализатор не может изменять свои значения.
Но с Json.NET вы можете написать собственный класс конвертера, который обрабатывает десериализацию Version
учебный класс.
Осторожно: этот код не был проверен очень хорошо...
public class VersionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// default serialization
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// create a new Version instance and pass the properties to the constructor
// (you may also use dynamics if you like)
var dict = serializer.Deserialize<Dictionary<string, int>>(reader);
return new Version(dict["Major"], dict["Minor"], dict["Build"], dict["Revision"]);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Version);
}
}
Затем вы должны указать, что вы хотите использовать конвертер:
var v = new Version(1, 2, 3, 4);
string json = JsonConvert.SerializeObject(v);
var v2 = JsonConvert.DeserializeObject<Version>(json, new VersionConverter());
Json.NET решает, использовать ли один из указанных вами преобразователей. Таким образом, вы всегда можете указать конвертер, как показано ниже. Json.NET будет использовать один из ваших конвертеров, если они соответствуют типу в SomeClass
,
var result = JsonConvert.DeserializeObject<SomeClass>(json, new VersionConverter());