C# Newtonsoft Json.NET InvalidCastException

C# Newtonsoft Json.NET InvalidCastException,c#,json.net,C#,Json.net,这更像是一个技术支持问题,但newtonsoft.com网站称stackoverflow是提问的最佳场所 我有一个用6.0.3序列化的字符串,但用6.0.8反序列化失败。它抛出InvalidCastException。当我降级到6.0.3,它反序列化罚款;当我再次升级到6.0.8时,异常是可重复的 编辑: 我没有发布实际的字符串(长度为10KB,包含敏感信息),而是创建了一个简单的可复制测试用例来演示问题 引发异常的行是: this.SomeStrings = (string[])infoEnu

这更像是一个技术支持问题,但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();
        }
    }
}

我对此做了一些调查,可以确认问题最初出现在中,并且在最新版本(撰写本文时为9.0.1)中仍然可以重现。该更改似乎是2014年11月4日承诺“支持ISerializable对象的引用保存”的一部分。根据,类的
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),因此这显然是反序列化端的回归。如果你还没有,你可能想在GitHub上。(是的,我意识到这个问题已经有一年半的历史了;我只是想在这里给出一些结论。;-)

作为一种解决方法,您可以从
SerializationEntry
中提取字符串数组,如下所示:

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

因此,如果看不到导致问题的确切字符串或显示完全相同行为的复制案例,您将无法帮助您。如果调试代码,您可以告诉我们infoEnum.Current.Value的类型吗?或者这是整个10KB长的字符串?infoEnum.Current.Value的类型是
object{Newtonsoft.Json.Linq.JObject}
,它的值是
{“$type”:“System.string[],mscorlib”,“$values”:[]}
。。。可能问题实际上是将字符串[]转换为字符串[]。。。否-我确信问题在于将Newtonsoft.Json.Linq.JObject强制转换为字符串[],即使JObject中的值明确指出它应该是字符串[]是-InvalidCastException中的更多信息说“无法将'Newtonsoft.Json.Linq.JObject'类型的对象强制转换为'System.string[]”