gpt4 book ai didi

c# - Newtonsoft Json.NET InvalidCastException

转载 作者:行者123 更新时间:2023-11-30 16:55:56 24 4
gpt4 key购买 nike

这更像是一个技术支持问题,但 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;以下只是为了从学术上说明错误行为的重现。

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();
}
}
}

最佳答案

我对此做了一些调查,可以确认问题首先出现在 version 6.0.7 中并且在最新版本(撰写本文时为 9.0.1)中仍然可以重现。该更改似乎是作为 2014 年 11 月 4 日“支持 ISerializable 对象的引用保存” promise 的一部分进行的。根据 source code diffs , JsonSerializerInternalReader 类的 CreateISerializable() 方法中的以下代码由此更改:

        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),所以这看起来肯定是反序列化端的回归。如果你还没有,你可能想要 report an issue在 GitHub 上。 (是的,我确实意识到这个问题已经有一年半的历史了;我只是想在这里提供一些结束语。;-))

作为解决方法,您可以像这样从 SerializationEntry 中提取字符串数组:

            this.SomeStrings = ((JObject)entry.Value)["$values"].ToObject<string[]>();

关于c# - Newtonsoft Json.NET InvalidCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28789089/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com