我正在尝试反序列化具有锯齿状和多维数组属性的对象:
public abstract class Foo {}
public class Baz
{
public readonly List<Foo> Foos;
public Baz()
{
Foos = new List<Foo>();
}
}
public class Bar : Foo
{
public readonly double[][,,] Values;
public Bar(double[][,,] values)
{
Values = values;
}
}
由于Baz
有一个List<Foo>
并且Foo
是一个抽象类,所以我想在序列化的字符串中保留Foo的类型,所以我必须使用TypeNameHandling.All
:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented
};
但是,当我运行以下代码时:
var barValues = new double[][,,] { new double[,,] {{{ 1 }}} };
var baz = new Baz();
baz.Foos.Add(new Bar(barValues));
var json = JsonConvert.SerializeObject(baz, settings);
var baz2 = JsonConvert.DeserializeObject<Baz>(json, settings);
我有一个例外:
在JSON'System.Double [,] [],System.Private.CoreLib, 版本= 4.0.0.0,文化=中性,PublicKeyToken = 7cec85d7bea7798e'是 与'System.Double [,,] [],System.Private.CoreLib不兼容, 版本= 4.0.0.0,文化=中性,PublicKeyToken = 7cec85d7bea7798e'。 路径'Foos。$ values [0] .Values。$ type',第9行,位置63。'
如果我检查序列化的字符串,它看起来很奇怪:
"Values": {
"$type": "System.Double[,][], System.Private.CoreLib",
...
}
在这种情况下,为什么JsonConvert
无法反序列化字符串?
答案 0 :(得分:2)
这似乎是TypeNameHandling.Arrays
和等级大于2的多维数组的错误。
通过使用TypeNameHandling.Arrays
序列化3d双精度数组,我可以更轻松地重现该问题:
var root = new double[,,] { { { 1 } } };
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Arrays };
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
// Try to deserialize to the same type as root
// but get an exception instead:
var root2 = JsonConvert.DeserializeAnonymousType(json, root, settings);
上面的代码生成的JSON是:
{
"$type": "System.Double[,], mscorlib",
"$values": [ [ [ 1.0 ] ] ]
}
可以预见到"$type"
属性的存在,并记录在 TypeNameHandling setting 中,但是正如您注意到的那样,它看起来是错误的:它应该在数组类型如下:
"$type": "System.Double[,,], mscorlib",
实际上,如果我像这样手动将[,]
替换为[,,]
,就可以成功反序列化JSON:
// No exception!
JsonConvert.DeserializeAnonymousType(json.Replace("[,]", "[,,]"), root, settings)
最后,如果我尝试使用2d阵列而不是3d阵列进行相同的测试,则测试通过。演示小提琴here。
原因在以下回溯调用中似乎是例程ReflectionUtils.RemoveAssemblyDetails
中的错误:
Newtonsoft.Json.Utilities.ReflectionUtils.RemoveAssemblyDetails(string) C#
Newtonsoft.Json.Utilities.ReflectionUtils.GetTypeName(System.Type, Newtonsoft.Json.TypeNameAssemblyFormatHandling, Newtonsoft.Json.Serialization.ISerializationBinder) C#
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteTypeProperty(Newtonsoft.Json.JsonWriter, System.Type) C#
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteStartArray(Newtonsoft.Json.JsonWriter, object, Newtonsoft.Json.Serialization.JsonArrayContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) C#
调用时,输入参数具有值
System.Double[,,], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
但是返回值是
System.Double[,], mscorlib
这显然是错误的。
如果需要,可以向Newtonsoft here报告问题。
更新:今天打开了一个类似的问题: Type of multi-dimensional array is incorrect #1918 。
一种解决方法,应该将输出类型信息的属性范围限制为给定JSON对象实际上可能是多态的情况。可能性包括:
您可以使用TypeNameHandling.None
序列化对象图,但是用JsonPropertyAttribute.ItemTypeNameHandling = TypeNameHandling.Auto
标记多态集合,如下所示:
public class Baz
{
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
public readonly List<Foo> Foos;
public Baz()
{
Foos = new List<Foo>();
}
}
此解决方案可减少JSON的膨胀,并最大程度地减少了 TypeNameHandling caution in Newtonsoft Json 和 External json vulnerable because of Json.Net TypeNameHandling auto?中描述的使用TypeNameHandling
的安全风险,因此是首选方案。
您可以使用TypeNameHandling.None
序列化对象图,并使用custom contract resolver将JsonArrayContract.ItemTypeNameHandling
设置为TypeNameHandling.Auto
用于具有潜在多态项的集合,方法是覆盖{{ 3}}。
如果无法将Json.NET属性添加到类型中,这将是解决方案。
您可以使用TypeNameHandling.Auto
或TypeNameHandling.Objects
序列化对象图。
这两个选项均可以避免该错误并减少JSON中的膨胀,但不会降低安全风险。
您可以使用DefaultContractResolver.CreateArrayContract
序列化对象图。
这避免了对RemoveAssemblyDetails()
的调用,但导致JSON更加膨胀,并且无法避免可能的安全风险。