这更像是一个技术支持问题,但是newtonsoft.com网站上说stackoverflow是提问的最好地方。
我有一个用6.0.3序列化的字符串,无法用6.0.8反序列化。它会抛出InvalidCastException。当我降级到6.0.3时,它会反序列化为罚款;当我再次升级到6.0.8时,异常是可重复的。
修改
我没有发布长度为10KB且包含敏感信息的实际字符串,而是创建了一个简单的可重现的测试用例来演示这个问题。
抛出异常的行是:
this.SomeStrings = (string[])infoEnum.Current.Value;
InvalidCastException表示"无法转换类型' Newtonsoft.Json.Linq.JObject'输入' System.String []'"
如下面的评论所述,我使用6.0.3序列化了一个FooClass实例,然后将该字符串硬编码为asdf()方法并尝试反序列化。反序列化在6.0.3上成功,在6.0.8上失败并出现InvalidCastException。
显然,在下面的普通复制案例中,在FooClass上进行ISerializable是没有意义的,但在现实生活中,我需要在复杂的数据类型中使用ISerializable,它将自身序列化和反序列化为字符串数组;以下只是在学术上说明了bug行为的再现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using System.Runtime.Serialization;
namespace CBClasses
{
[Serializable]
public class FooClass : ISerializable
{
public string[] SomeStrings { get; set; }
public FooClass() { }
protected FooClass(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new ArgumentNullException();
SerializationInfoEnumerator infoEnum = info.GetEnumerator();
while (infoEnum.MoveNext()) {
SerializationEntry entry = infoEnum.Current;
switch (entry.Name) {
case "SomeStrings":
this.SomeStrings = (string[])infoEnum.Current.Value;
break;
default:
throw new SerializationException("Deserialization failed on unhandled member '" + entry.Name + "'");
}
}
}
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("SomeStrings", this.SomeStrings, this.SomeStrings.GetType());
}
}
public class NewtonsoftDebugTest
{
private static JsonSerializerSettings settings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
};
public static void asdf()
{
/* FooClass foo = new FooClass() { SomeStrings = new string[0] };
* string serializedBy603 = JsonConvert.SerializeObject(foo, settings);
* Resulted in:
*
* {
* "$id": "1",
* "$type": "CBClasses.FooClass, CBClasses",
* "SomeStrings": {
* "$type": "System.String[], mscorlib",
* "$values": []
* }
* }
*
* Now hard-coded below:
*/
string serializedBy603 =
"{\n" +
" \"$id\": \"1\",\n" +
" \"$type\": \"CBClasses.FooClass, CBClasses\",\n" +
" \"SomeStrings\": {\n" +
" \"$type\": \"System.String[], mscorlib\",\n" +
" \"$values\": []\n" +
" }\n" +
"}\n";
FooClass deserialized = (FooClass)JsonConvert.DeserializeObject(serializedBy603, settings);
System.Diagnostics.Debugger.Break();
}
}
}
答案 0 :(得分:2)
我对此做了一些调查,可以确认问题最初出现在version 6.0.7中,并且仍然可以与最新版本(撰写本文时为9.0.1)重现。该更改似乎是作为提交“#IS;支持ISerializable对象的参考保存”的一部分而进行的。从2014年11月4日开始。根据source code diffs,CreateISerializable()
类的JsonSerializerInternalReader
方法中的以下代码已从此更改:
if (reader.TokenType == JsonToken.StartObject)
{
// this will read any potential type names embedded in json
object o = CreateObject(reader, null, null, null, contract, member, null);
serializationInfo.AddValue(memberName, o);
}
else
{
serializationInfo.AddValue(memberName, JToken.ReadFrom(reader));
}
到此:
serializationInfo.AddValue(memberName, JToken.ReadFrom(reader));
似乎很清楚,前一代码用于处理嵌入式类型元数据,而替换代码则不然。而且,事实上,我可以确认将这一部分代码恢复到原始状态可以解决问题。但是,在不知道此更改的意图的情况下(可能元数据应该在代码中的其他位置处理?),我不能建议盲目地还原它,因为这可能会破坏其他内容。当前的序列化代码仍然像以前一样添加类型元数据(我获得与问题中发布的相同的JSON),因此这似乎是反序列化结束时的回归。如果您还没有,可能需要在GitHub上report an issue。 (是的,我确实意识到这个问题超过一年半;我只是想在这里提供一些关闭。; - ))
作为一种解决方法,您可以从SerializationEntry
中提取字符串数组,如下所示:
this.SomeStrings = ((JObject)entry.Value)["$values"].ToObject<string[]>();