Newtonsoft.Json 在 .NET 5/Core 和 .NET Framework 上的不同行为
Different behaviour of Newtonsoft.Json on .NET 5 / Core and .NET Framework
以下程序在 .NET 5(或 .NET Core)和 .NET Framework 上 运行 时会产生不同的结果。
为什么行为不同?我不是在解决反序列化问题之后;我的目标是了解发生了什么。
class Versioned
{
public Version V {get; set;} = new Version(1,0);
}
static void Main(string[] args)
{
// Serialised with version Newtonsoft.Json 9.0.1
var json = "{\"V\":{\"Major\":2,\"Minor\":0,\"Build\":-1,\"Revision\":-1,\"MajorRevision\":-1,\"MinorRevision\":-1}}";
Console.WriteLine($".NET: {System.Environment.Version}");
Console.WriteLine($"Json.NET: {System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.JsonConvert))}");
Console.WriteLine(json);
try
{
var b = Newtonsoft.Json.JsonConvert.DeserializeObject<Versioned>(json);
Console.WriteLine(b.V);
}
catch (Exception ex) { Console.WriteLine(ex.GetBaseException().Message); }
}
在 .NET 5 上:输出为(为了便于阅读而格式化):
.NET: 5.0.1
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
"V": {
"Major": 2,
"Minor": 0,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
}
}
出现此错误:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Version' because the type requires a JSON string value to deserialize correctly.
To fix this error either change the JSON to a JSON string value or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'V.Major', line 1, position 14.
在 .NET Framework 4.6.2 上,输出为:
.NET: 4.0.30319.42000
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
"V": {
"Major": 2,
"Minor": 0,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
}
}
1.0
此外,由于没有默认值,.NET 4.6.2 上的行为再次不同。
class Versioned
{
public Version V { get; set; }// = new Version(1, 0);
}
在 .NET 5.0 上输出是相同的:
(...)
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Version' because...
在 .NET Framework 4.6.2 上,输出现在是:
(...)
Version's parameters must be greater than or equal to zero.
Parameter name: build
据我所知,System.Version
class 在这些 .NET 版本之间(在外部)没有改变,应该在这里发挥作用(我只知道ISpanFormattable
差异)。我查看了源代码 (.NET Core version and .NET Framework 4.8 version),但我看不到任何可以解释不同行为的内容。
看起来 Json.NET 在 .NET Core 2.2 中有一个新的 VersionConverter
,它知道如何正确序列化和反序列化 Version
实例。当您使用 .NET Core 2.2+ 时,它会自动被拾取和使用。
使用 VersionConverter
,Json.NET 想要将您的 Version
对象序列化为 "1.0"
之类的字符串,而不是 JSON 对象。如果您通过在 .NET Core 2.2+ 上序列化 Versioned
的新实例在 post 中创建 json
字符串:
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new Versioned());
你会看到它 returns 例如{"V":"1.0"}
.
同样,VersionConverter
只知道如何读取版本字符串,例如 "1.0"
,而不知道如何处理包含 Major
、Minor
的对象等
以下程序在 .NET 5(或 .NET Core)和 .NET Framework 上 运行 时会产生不同的结果。
为什么行为不同?我不是在解决反序列化问题之后;我的目标是了解发生了什么。
class Versioned
{
public Version V {get; set;} = new Version(1,0);
}
static void Main(string[] args)
{
// Serialised with version Newtonsoft.Json 9.0.1
var json = "{\"V\":{\"Major\":2,\"Minor\":0,\"Build\":-1,\"Revision\":-1,\"MajorRevision\":-1,\"MinorRevision\":-1}}";
Console.WriteLine($".NET: {System.Environment.Version}");
Console.WriteLine($"Json.NET: {System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.JsonConvert))}");
Console.WriteLine(json);
try
{
var b = Newtonsoft.Json.JsonConvert.DeserializeObject<Versioned>(json);
Console.WriteLine(b.V);
}
catch (Exception ex) { Console.WriteLine(ex.GetBaseException().Message); }
}
在 .NET 5 上:输出为(为了便于阅读而格式化):
.NET: 5.0.1
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
"V": {
"Major": 2,
"Minor": 0,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
}
}
出现此错误:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Version' because the type requires a JSON string value to deserialize correctly.
To fix this error either change the JSON to a JSON string value or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'V.Major', line 1, position 14.
在 .NET Framework 4.6.2 上,输出为:
.NET: 4.0.30319.42000
Json.NET: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
{
"V": {
"Major": 2,
"Minor": 0,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
}
}
1.0
此外,由于没有默认值,.NET 4.6.2 上的行为再次不同。
class Versioned
{
public Version V { get; set; }// = new Version(1, 0);
}
在 .NET 5.0 上输出是相同的:
(...)
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Version' because...
在 .NET Framework 4.6.2 上,输出现在是:
(...)
Version's parameters must be greater than or equal to zero.
Parameter name: build
据我所知,System.Version
class 在这些 .NET 版本之间(在外部)没有改变,应该在这里发挥作用(我只知道ISpanFormattable
差异)。我查看了源代码 (.NET Core version and .NET Framework 4.8 version),但我看不到任何可以解释不同行为的内容。
看起来 Json.NET 在 .NET Core 2.2 中有一个新的 VersionConverter
,它知道如何正确序列化和反序列化 Version
实例。当您使用 .NET Core 2.2+ 时,它会自动被拾取和使用。
使用 VersionConverter
,Json.NET 想要将您的 Version
对象序列化为 "1.0"
之类的字符串,而不是 JSON 对象。如果您通过在 .NET Core 2.2+ 上序列化 Versioned
的新实例在 post 中创建 json
字符串:
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new Versioned());
你会看到它 returns 例如{"V":"1.0"}
.
同样,VersionConverter
只知道如何读取版本字符串,例如 "1.0"
,而不知道如何处理包含 Major
、Minor
的对象等