Newtonsoft Json 序列化程序无法反序列化 LinqPad 中定义的类型
Types defined in LinqPad cannot be deserialized by Newtonsoft Json serializer
我有一个 class,我在 Rebus 中将其用作 publish/subscribe 的消息类型,但是当我在 LinqPad 中尝试概念验证时遇到了障碍。我的应用程序收到的任何消息都因反序列化异常而失败。我已经能够将问题缩小到 Newtonsoft.JSON 包,并想出最小的例子来证明这个问题:
public class MyMessage
{
public string Name { get; set; } = "";
public int Number { get; set; }
}
void Main()
{
var message = new MyMessage { Name = "ABC", Number = 5 };
var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var serialized = JsonConvert.SerializeObject(message, defaultSettings);
Console.WriteLine(serialized);
try
{
var deserialized = JsonConvert.DeserializeObject(serialized, message.GetType(), defaultSettings);
Console.WriteLine(deserialized);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
如果我在 Rider 中编译此代码作为控制台应用程序,它可以正常工作,并将以下内容输出到控制台:
{"$type":"ConsoleApp1.MyMessage, ConsoleApp1","Name":"ABC","Number":5}
ConsoleApp1.MyMessage
LinqPad 6 中的相同代码在具有 JsonConvert.DeserializeObject
语句的行中失败,并出现以下错误消息:
{"$type":"UserQuery+MyMessage, LINQPadQuery","Name":"ABC","Number":5}
Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null'. Path '$type', line 1, position 44.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at UserQuery.Main() in C:\Users\arcaa\AppData\Local\Temp\LINQPad6\_dwfnlfnz\tufqxf\LINQPadQuery:line 19
异常消息:Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null
看起来好像序列化程序正在尝试将字符串反序列化为与作为参数提供的类型 (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61
) 不同的类型版本 (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5
)。这里有趣的一点是 JsonConvert.DeserializeObject
方法中使用的类型与传递给序列化程序的类型完全相同,并且该代码中只有一个 class。
我不知道其他类型的版本来自哪里。有谁知道造成这种情况的原因,如果可能的话,如何避免这种情况?
编辑:
我还注意到,如果我关闭 LinqPad 并再次打开它,代码可以正常工作。但是如果我然后去更改代码(即使我只添加一个 space),异常 returns.
最有可能的是,Newtonsoft.Json 库中会有一些静态字典来缓存类型信息,键入诸如“MyMessage”之类的字符串。当您重新编辑查询 运行 时,LINQPad 必须重新编译查询,因此新版本的 MyMessage
将位于新的 DLL 中。但是,Newtonsoft.Json 中的静态缓存仍将指向上次执行时使用的旧缓存。
要解决这个问题,您不需要重新启动 LINQPad;只需按 Shift+F5
即可清除缓存的进程。或者,将以下代码添加到您的查询中:
Util.NewProcess = true;
这告诉 LINQPad 不要缓存进程以供后续使用。
编辑中还有一个选项 |首选项 > 高级以永不回收进程。这会产生很小的性能成本; LINQPad 的 Util.Cache 等方法(在执行之间缓存数据)也将不起作用。
我有一个 class,我在 Rebus 中将其用作 publish/subscribe 的消息类型,但是当我在 LinqPad 中尝试概念验证时遇到了障碍。我的应用程序收到的任何消息都因反序列化异常而失败。我已经能够将问题缩小到 Newtonsoft.JSON 包,并想出最小的例子来证明这个问题:
public class MyMessage
{
public string Name { get; set; } = "";
public int Number { get; set; }
}
void Main()
{
var message = new MyMessage { Name = "ABC", Number = 5 };
var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var serialized = JsonConvert.SerializeObject(message, defaultSettings);
Console.WriteLine(serialized);
try
{
var deserialized = JsonConvert.DeserializeObject(serialized, message.GetType(), defaultSettings);
Console.WriteLine(deserialized);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
如果我在 Rider 中编译此代码作为控制台应用程序,它可以正常工作,并将以下内容输出到控制台:
{"$type":"ConsoleApp1.MyMessage, ConsoleApp1","Name":"ABC","Number":5}
ConsoleApp1.MyMessage
LinqPad 6 中的相同代码在具有 JsonConvert.DeserializeObject
语句的行中失败,并出现以下错误消息:
{"$type":"UserQuery+MyMessage, LINQPadQuery","Name":"ABC","Number":5}
Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null'. Path '$type', line 1, position 44.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at UserQuery.Main() in C:\Users\arcaa\AppData\Local\Temp\LINQPad6\_dwfnlfnz\tufqxf\LINQPadQuery:line 19
异常消息:Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null
看起来好像序列化程序正在尝试将字符串反序列化为与作为参数提供的类型 (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61
) 不同的类型版本 (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5
)。这里有趣的一点是 JsonConvert.DeserializeObject
方法中使用的类型与传递给序列化程序的类型完全相同,并且该代码中只有一个 class。
我不知道其他类型的版本来自哪里。有谁知道造成这种情况的原因,如果可能的话,如何避免这种情况?
编辑:
我还注意到,如果我关闭 LinqPad 并再次打开它,代码可以正常工作。但是如果我然后去更改代码(即使我只添加一个 space),异常 returns.
最有可能的是,Newtonsoft.Json 库中会有一些静态字典来缓存类型信息,键入诸如“MyMessage”之类的字符串。当您重新编辑查询 运行 时,LINQPad 必须重新编译查询,因此新版本的 MyMessage
将位于新的 DLL 中。但是,Newtonsoft.Json 中的静态缓存仍将指向上次执行时使用的旧缓存。
要解决这个问题,您不需要重新启动 LINQPad;只需按 Shift+F5
即可清除缓存的进程。或者,将以下代码添加到您的查询中:
Util.NewProcess = true;
这告诉 LINQPad 不要缓存进程以供后续使用。
编辑中还有一个选项 |首选项 > 高级以永不回收进程。这会产生很小的性能成本; LINQPad 的 Util.Cache 等方法(在执行之间缓存数据)也将不起作用。