【问题标题】:JsonConvert serialize/deserialize jagged array with TypeNameHandling enabled启用 TypeNameHandling 的 JsonConvert 序列化/反序列化锯齿状数组
【发布时间】:2019-04-21 10:09:41
【问题描述】:

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

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&lt;Foo&gt; 而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在这种情况下不能反序列化字符串?

【问题讨论】:

    标签: c# json serialization json.net


    【解决方案1】:

    这似乎是 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 对象在实践中可能是多态的情况。可能性包括:

    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 caution in Newtonsoft Json 和 External json vulnerable because of Json.Net TypeNameHandling auto? 中描述的 TypeNameHandling 的安全风险降至最低,因此是最好的解决方案。

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2021-01-20
      • 2020-04-04
      • 1970-01-01
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多