此示例代码:
var json = JsonConvert.SerializeObject(new XmlException("bla"));
var exception = JsonConvert.DeserializeObject<XmlException>(json);
Run Code Online (Sandbox Code Playgroud)
在Newtonsoft.Json.dll中抛出InvalidCastException:无法将类型为"Newtonsoft.Json.Linq.JValue"的对象强制转换为使用以下堆栈跟踪键入"System.String":
at System.Xml.XmlException..ctor(SerializationInfo info, StreamingContext context)
at Void .ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)(Object[] )
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateISerializable(JsonReader reader, JsonISerializableContract contract, JsonProperty member, 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.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)
at TestJson.Program.Main(String[] args) in C:\Projects\TestJson\TestJson\Program.cs:line 21
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Run Code Online (Sandbox Code Playgroud)
我错过了什么吗?
在https://github.com/JamesNK/Newtonsoft.Json/issues/801上创建了一个问题
问题
\n\n这里的基本问题是弱类型的 JSON 和最初设计用于强类型流的ISerializabe+之间的不兼容。即,有时期望序列化流包含序列化字段的完整类型信息。事实证明有一个这样的实现。SerializationInfoBinaryFormatterISerializableXmlException
具体如下。当 Json.NET 调用类型的序列化构造函数ISerializable时,它会构造 aSerializationInfo并传递 a ,该 a 应该在调用JsonFormatterConverter时处理从 JSON 数据转换为所需类型的工作。SerializationInfo.GetValue(String,\xe2\x80\x82Type)现在,当找不到指定值时,此方法会引发异常。而且,不幸的是,没有SerializationInfo.TryGetValue()方法要求需要反序列化可选字段的类使用 手动循环属性GetEnumerator()。但此外,没有方法可以检索构造函数中设置的转换器,这意味着可选字段无法在需要时进行转换,因此必须已使用精确的预期类型进行反序列化。
您可以在构造函数的参考源中看到这一点XmlException:
protected XmlException(SerializationInfo info, StreamingContext context) : base(info, context) {\n res = (string) info.GetValue("res" , typeof(string));\n args = (string[])info.GetValue("args", typeof(string[]));\n lineNumber = (int) info.GetValue("lineNumber", typeof(int));\n linePosition = (int) info.GetValue("linePosition", typeof(int));\n\n // deserialize optional members\n sourceUri = string.Empty;\n string version = null;\n foreach ( SerializationEntry e in info ) {\n switch ( e.Name ) {\n case "sourceUri":\n sourceUri = (string)e.Value;\n break;\n case "version":\n version = (string)e.Value;\n break;\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n\n事实证明这e.Value仍然JValue不是一个string此时
Json.NET 可以解决这个特定问题,方法是在 in构造其 时JsonSerializerInternalReader.CreateISerializable()将字符串值标记替换为实际字符串,然后在需要转换时重新转换为in 。然而,这并不能解决此类问题。例如,当 an被 Json.NET 往返时,它会变成 a ,如果在没有转换的情况下进行强制转换,则会抛出异常。当然,一个字段会在没有转换的情况下抛出。这也将是一个突破性的改变JValueSerializationInfoJValueJsonFormatterConverterintlongDateTimeISerializable以前手工制作的与 Json.NET 一起使用的类可能会被破坏。
您可能会报告问题,但我怀疑它会很快得到解决。
\n\n解决该问题的更可靠的方法是创建一个JsonConverter嵌入完整类型信息的自定义ISerializable类型完整类型信息的自定义。
解决方案 1:嵌入二进制文件
\n\n第一个最简单的解决方案是将BinaryFormatter流嵌入到 JSON 中。类的序列化代码Exception最初设计为兼容BinaryFormatter,因此这应该相当可靠:
public class BinaryConverter<T> : JsonConverter where T : ISerializable\n{\n class BinaryData\n {\n public byte[] binaryData { get; set; }\n }\n\n public override bool CanConvert(Type objectType)\n {\n return typeof(T).IsAssignableFrom(objectType);\n }\n\n public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n {\n if (reader.TokenType == JsonToken.Null)\n return null;\n var data = serializer.Deserialize<BinaryData>(reader);\n if (data == null || data.binaryData == null)\n return null;\n return BinaryFormatterHelper.FromByteArray<T>(data.binaryData);\n\n }\n\n public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n {\n var data = new BinaryData { binaryData = BinaryFormatterHelper.ToByteArray(value) };\n serializer.Serialize(writer, data);\n }\n}\n\npublic static class BinaryFormatterHelper\n{\n public static byte [] ToByteArray<T>(T obj)\n {\n using (var stream = new MemoryStream())\n {\n new BinaryFormatter().Serialize(stream, obj);\n return stream.ToArray();\n }\n }\n\n public static T FromByteArray<T>(byte[] data)\n {\n return FromByteArray<T>(data, null);\n }\n\n public static T FromByteArray<T>(byte[] data, BinaryFormatter formatter)\n {\n using (var stream = new MemoryStream(data))\n {\n formatter = (formatter ?? new BinaryFormatter());\n var obj = formatter.Deserialize(stream);\n if (obj is T)\n return (T)obj;\n return default(T);\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后使用以下设置进行序列化:
\n\nvar settings = new JsonSerializerSettings { Converters = new[] { new BinaryConverter<Exception>() } };\nRun Code Online (Sandbox Code Playgroud)\n\n缺点是:
\n\n反序列化不受信任的数据存在严重的安全隐患。由于类型信息完全嵌入到专有的、不可读的序列化流中,因此在完成此操作之前,您无法知道要构造什么。
JSON 完全不可读。
我相信BinaryFormatter某些 .Net 版本中缺少此功能。
相信BinaryFormatter只有完全信任才能使用。
但是,如果您想做的只是在您控制的进程之间序列化异常,那么这可能就足够了。
\n\n解决方案 2:使用 嵌入类型信息TypeNameHandling。
JsonSerializer.TypeNameHandlingJson.NET 还具有通过设置适当的值在序列化流中嵌入非基本类型的 .NET 类型信息的可选功能。使用此功能以及原始类型的包装器,可以创建JsonConverter封装SerializationInfo并SerializationEntry包含所有已知类型信息的 :
public class ISerializableConverter<T> : JsonConverter where T : ISerializable\n{\n public override bool CanConvert(Type objectType)\n {\n return typeof(T).IsAssignableFrom(objectType);\n }\n\n public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)\n {\n if (reader.TokenType == JsonToken.Null)\n return null;\n var oldTypeNameHandling = serializer.TypeNameHandling;\n var oldAssemblyFormat = serializer.TypeNameAssemblyFormat;\n try\n {\n if (serializer.TypeNameHandling == TypeNameHandling.None)\n serializer.TypeNameHandling = TypeNameHandling.Auto;\n else if (serializer.TypeNameHandling == TypeNameHandling.Arrays)\n serializer.TypeNameHandling = TypeNameHandling.All;\n var data = serializer.Deserialize<SerializableData>(reader);\n var type = data.ObjectType;\n var info = new SerializationInfo(type, new FormatterConverter());\n foreach (var item in data.Values)\n info.AddValue(item.Key, item.Value.ObjectValue, item.Value.ObjectType);\n var value = Activator.CreateInstance(type, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { info, serializer.Context }, serializer.Culture);\n if (value is IObjectReference)\n value = ((IObjectReference)value).GetRealObject(serializer.Context);\n return value;\n }\n finally\n {\n serializer.TypeNameHandling = oldTypeNameHandling;\n serializer.TypeNameAssemblyFormat = oldAssemblyFormat;\n }\n }\n\n public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)\n {\n var oldTypeNameHandling = serializer.TypeNameHandling;\n var oldAssemblyFormat = serializer.TypeNameAssemblyFormat;\n try\n {\n var serializable = (ISerializable)value;\n var context = serializer.Context;\n var info = new SerializationInfo(value.GetType(), new FormatterConverter());\n serializable.GetObjectData(info, context);\n var data = SerializableData.CreateData(info, value.GetType());\n\n if (serializer.TypeNameHandling == TypeNameHandling.None)\n serializer.TypeNameHandling = TypeNameHandling.Auto;\n else if (serializer.TypeNameHandling == TypeNameHandling.Arrays)\n serializer.TypeNameHandling = TypeNameHandling.All;\n // The following seems to be required by https://github.com/JamesNK/Newtonsoft.Json/issues/787\n serializer.TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full;\n serializer.Serialize(writer, data, typeof(SerializableData));\n }\n finally\n {\n serializer.TypeNameHandling = oldTypeNameHandling;\n serializer.TypeNameAssemblyFormat = oldAssemblyFormat;\n }\n }\n}\n\nabstract class SerializableValue\n{\n [JsonIgnore]\n public abstract object ObjectValue { get; }\n\n [JsonIgnore]\n public abstract Type ObjectType { get; }\n\n public static SerializableValue CreateValue(SerializationEntry entry)\n {\n return CreateValue(entry.ObjectType, entry.Value);\n }\n\n public static SerializableValue CreateValue(Type type, object value)\n {\n if (value == null)\n {\n if (type == null)\n throw new ArgumentException("type and value are both null");\n return (SerializableValue)Activator.CreateInstance(typeof(SerializableValue<>).MakeGenericType(type));\n }\n else\n {\n type = value.GetType(); // Use most derived type\n return (SerializableValue)Activator.CreateInstance(typeof(SerializableValue<>).MakeGenericType(type), value);\n }\n }\n}\n\nsealed class SerializableValue<T> : SerializableValue\n{\n public SerializableValue() : base() { }\n\n public SerializableValue(T value)\n : base()\n {\n this.Value = value;\n }\n\n public override object ObjectValue { get { return Value; } }\n\n public override Type ObjectType { get { return typeof(T); } }\n\n [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]\n public T Value { get; private set; }\n}\n\nabstract class SerializableData\n{\n public SerializableData()\n {\n this.Values = new Dictionary<string, SerializableValue>();\n }\n\n public SerializableData(IEnumerable<SerializationEntry> values)\n {\n this.Values = values.ToDictionary(v => v.Name, v => SerializableValue.CreateValue(v));\n }\n\n [JsonProperty("values", ItemTypeNameHandling = TypeNameHandling.Auto)]\n public Dictionary<string, SerializableValue> Values { get; private set; }\n\n [JsonIgnore]\n public abstract Type ObjectType { get; }\n\n public static SerializableData CreateData(SerializationInfo info, Type initialType)\n {\n if (info == null)\n throw new ArgumentNullException("info");\n var type = info.GetSavedType(initialType);\n if (type == null)\n throw new InvalidOperationException("type == null");\n return (SerializableData)Activator.CreateInstance(typeof(SerializableData<>).MakeGenericType(type), info.AsEnumerable());\n }\n}\n\nsealed class SerializableData<T> : SerializableData\n{\n public SerializableData() : base() { }\n\n public SerializableData(IEnumerable<SerializationEntry> values) : base(values) { }\n\n public override Type ObjectType { get { return typeof(T); } }\n}\n\npublic static class SerializationInfoExtensions\n{\n public static IEnumerable<SerializationEntry> AsEnumerable(this SerializationInfo info)\n {\n if (info == null)\n throw new NullReferenceException();\n var enumerator = info.GetEnumerator();\n while (enumerator.MoveNext())\n {\n yield return enumerator.Current;\n }\n }\n\n public static Type GetSavedType(this SerializationInfo info, Type initialType)\n {\n if (initialType != null)\n {\n if (info.FullTypeName == initialType.FullName\n && info.AssemblyName == initialType.Module.Assembly.FullName)\n return initialType;\n }\n var assembly = Assembly.Load(info.AssemblyName);\n if (assembly != null)\n {\n var type = assembly.GetType(info.FullTypeName);\n if (type != null)\n return type;\n }\n return initialType;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n然后使用以下设置:
\n\n这会生成如下所示的半可读 JSON:
\n\n\n\n\nRun Code Online (Sandbox Code Playgroud)\n{\n "$type": "Question35015357.SerializableData`1[[System.Xml.XmlException, System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "values": {\n "ClassName": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": "System.Xml.XmlException"\n },\n "Message": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": "bla"\n },\n "Data": {\n "$type": "Question35015357.SerializableValue`1[[System.Collections.IDictionary, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "InnerException": {\n "$type": "Question35015357.SerializableValue`1[[System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "HelpURL": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "StackTraceString": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "RemoteStackTraceString": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "RemoteStackIndex": {\n "$type": "Question35015357.SerializableValue`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": 0\n },\n "ExceptionMethod": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "HResult": {\n "$type": "Question35015357.SerializableValue`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": -2146232000\n },\n "Source": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "res": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": "Xml_UserException"\n },\n "args": {\n "$type": "Question35015357.SerializableValue`1[[System.String[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": [\n "bla"\n ]\n },\n "lineNumber": {\n "$type": "Question35015357.SerializableValue`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": 0\n },\n "linePosition": {\n "$type": "Question35015357.SerializableValue`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": 0\n },\n "sourceUri": {\n "$type": "Question35015357.SerializableValue`1[[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"\n },\n "version": {\n "$type": "Question35015357.SerializableValue`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",\n "value": "2.0"\n }\n }\n}\n
正如您所看到的,JSON 的可读性在一定程度上减轻了安全隐患。您还可以创建自定义SerializationBinder以进一步减少仅加载预期类型的安全隐患,如Newtonsoft Json 中的 TypeNameHandling 警告中所述。
我不确定在部分信任的情况下应该做什么。 JsonSerializerInternalReader.CreateISerializable()产生部分信任:
private object CreateISerializable(JsonReader reader, JsonISerializableContract contract, JsonProperty member, string id)\n {\n Type objectType = contract.UnderlyingType;\n\n if (!JsonTypeReflector.FullyTrusted)\n {\n string message = @"Type \'{0}\' implements ISerializable but cannot be deserialized using
| 归档时间: |
|
| 查看次数: |
1407 次 |
| 最近记录: |