gpt4 book ai didi

c# - 启用 TypeNameHandling 的 JsonConvert 序列化/反序列化锯齿状数组

转载 作者:行者123 更新时间:2023-11-30 22:57:50 25 4
gpt4 key购买 nike

我正在尝试反序列化具有锯齿状和多维数组属性的对象:

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

我有一个异常(exception):

Type specified in JSON 'System.Double[,][], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' is not compatible with 'System.Double[,,][], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'. Path 'Foos.$values[0].Values.$type', line 9, position 63.'

如果我检查序列化字符串,它看起来很奇怪:

"Values": {
"$type": "System.Double[,][], System.Private.CoreLib",
...
}

为什么 JsonConvert在那种情况下不能反序列化字符串?

最佳答案

这似乎是 TypeNameHandling.Arrays 和等级 > 2 的多维数组的错误。

通过使用 TypeNameHandling.Arrays 序列化一个 3d double 组,我可以更轻松地重现该问题:

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 数组尝试相同的测试,则测试通过。演示 fiddle 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 对象实际上可能是多态的情况。可能性包括:

  1. 您可以使用 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 的安全风险, TypeNameHandling caution in Newtonsoft Json 中对此进行了描述。 External json vulnerable because of Json.Net TypeNameHandling auto? 因此是更可取的解决方案。

  2. 您可以使用 TypeNameHandling.None 序列化您的对象图并使用 custom contract resolver设置 JsonArrayContract.ItemTypeNameHandlingTypeNameHandling.Auto 用于具有潜在多态项的集合,通过覆盖 DefaultContractResolver.CreateArrayContract .

    如果您无法将 Json.NET 属性添加到您的类型,这将是可以使用的解决方案。

  3. 您可以使用 TypeNameHandling.AutoTypeNameHandling.Objects 序列化您的对象图。

    任一选项都可以避免错误并减少 JSON 中的膨胀,但不会降低安全风险。

  4. 您可以使用 JsonSerializerSettings.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full 序列化您的对象图.

    这避免了对 RemoveAssemblyDetails() 的调用,但会导致 JSON 更加臃肿,并且无法避免可能的安全风险。

关于c# - 启用 TypeNameHandling 的 JsonConvert 序列化/反序列化锯齿状数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53368034/

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