这似乎是 TypeNameHandling.Arrays 和 rank > 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,以通过覆盖DefaultContractResolver.CreateArrayContract 来处理具有潜在多态项的集合。
如果您无法将 Json.NET 属性添加到您的类型,这将是使用的解决方案。
-
您可以使用TypeNameHandling.Auto 或TypeNameHandling.Objects 序列化您的对象图。
任何一个选项都可以避免错误并减少 JSON 中的膨胀,但不会降低安全风险。
-
您可以使用JsonSerializerSettings.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full 序列化您的对象图。
这避免了对 RemoveAssemblyDetails() 的调用,但会导致 JSON 更加臃肿,并且无法避免可能的安全风险。