首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Json.net中使用非默认构造函数破坏反序列化顺序

在Json.net中使用非默认构造函数破坏反序列化顺序
EN

Stack Overflow用户
提问于 2016-04-26 13:18:39
回答 1查看 2K关注 0票数 3

当使用Json.net反序列化带有父子关系的对象图时,非默认构造函数的使用会打破反序列化的顺序,以便在其父对象之前反序列化子对象(构造和分配属性),从而导致空引用。

从实验中可以看出,所有非默认构造函数对象只在所有默认构造函数对象之后被实例化,奇怪的是,它似乎与序列化(父级之前的子对象)相反。

这将导致应该具有对其父对象(并正确序列化)的引用的“子”对象被反序列化为空值。

这似乎是一个非常常见的场景,所以我想知道我是否遗漏了什么?

有没有改变这种行为的环境?它是为其他场景设计的吗?除了全面创建默认构造函数之外,还有其他解决办法吗?

使用LINQPad或DotNetFiddle的简单示例

代码语言:javascript
运行
复制
void Main()
{
    var root = new Root();
    var middle = new Middle(1);
    var child = new Child();

    root.Middle = middle;
    middle.Root = root;
    middle.Child = child;
    child.Middle = middle;

    var json = JsonConvert.SerializeObject(root, new JsonSerializerSettings
    {
        Formatting = Newtonsoft.Json.Formatting.Indented,
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        PreserveReferencesHandling = PreserveReferencesHandling.All,        
        TypeNameHandling = TypeNameHandling.All,
    });

    json.Dump();

    //I have tried many different combinations of settings, but they all
    //seem to produce the same effect: 
    var deserialized = JsonConvert.DeserializeObject<Root>(json);

    deserialized.Dump();
}

public class Root
{
    public Root(){"Root".Dump();}

    public Middle Middle {get;set;}
}

public class Middle
{
    //Uncomment to see correct functioning:
    //public Middle(){"Middle".Dump();}

    public Middle(int foo){"Middle".Dump();}

    public Root Root {get;set;}

    public Child Child {get;set;}
}

public class Child
{
    public Child(){"Child".Dump();}

    public Middle Middle {get;set;}
}

JSON产出:

代码语言:javascript
运行
复制
{
  "$id": "1",
  "$type": "Root",
  "Middle": {
    "$id": "2",
    "$type": "Middle",
    "Root": {
      "$ref": "1"
    },
    "Child": {
      "$id": "3",
      "$type": "Child",
      "Middle": {
        "$ref": "2"
      }
    }
  }
}

具有非默认构造函数的中间输出:

代码语言:javascript
运行
复制
Root
Child
Middle
Child.Middle = null

具有默认构造函数的中东输出:

代码语言:javascript
运行
复制
Root
Middle
Child
Child.Middle = Middle
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-04-26 17:07:47

您需要使用与序列化相同的反序列化设置。尽管如此,您似乎在Json.NET中遇到了一个错误或限制。

发生这种情况的原因如下。如果您的Middle类型没有公共的无参数构造函数,但是有一个带有参数的公共构造函数,JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters()将调用该构造函数,通过名称将构造函数参数与JSON属性匹配,并对缺少的属性使用默认值。然后,所有剩余的未使用的JSON属性都将被设置为该类型。这使得只读属性的反序列化成为可能.例如,如果我将只读属性Foo添加到您的Middle类中:

代码语言:javascript
运行
复制
public class Middle
{
    readonly int foo;

    public int Foo { get { return foo; } }

    public Middle(int Foo) { this.foo = Foo; "Middle".Dump(); }

    public Root Root { get; set; }

    public Child Child { get; set; }
}

Foo的值将被成功反序列化。( JSON属性名称与构造函数参数名称的匹配在文档中显示为这里,但没有得到很好的解释。)

但是,这个功能似乎会干扰PreserveReferencesHandling.All。由于CreateObjectUsingCreatorWithParameters()完全反序列化正在构造的对象的所有子对象,以便将这些子对象传递到其构造函数中,如果子对象有一个"$ref"给它,则该引用将不会被解析,因为该对象尚未被构造。

作为解决办法,您可以将私有构造函数添加到Middle类型并设置ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor

代码语言:javascript
运行
复制
public class Middle
{
    private Middle() { "Middle".Dump(); }

    public Middle(int Foo) { "Middle".Dump(); }

    public Root Root { get; set; }

    public Child Child { get; set; }
}

然后:

代码语言:javascript
运行
复制
var settings = new JsonSerializerSettings
{
    Formatting = Newtonsoft.Json.Formatting.Indented,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    PreserveReferencesHandling = PreserveReferencesHandling.All,
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
};
var deserialized = JsonConvert.DeserializeObject<Root>(json, settings);

当然,如果这样做,您就失去了反序列化Middle的只读属性(如果有的话)的能力。

你可能想在这件事上做报告问题。理论上,当使用参数化构造函数反序列化类型时,以牺牲更高内存使用率为代价,Json.NET可以:

  • 将所有子JSON属性加载到中间JToken中。
  • 只将所需的参数反序列化为构造函数参数。
  • 构造对象。
  • 将对象添加到JsonSerializer.ReferenceResolver中。
  • 反序列化并设置其余属性。

但是,如果任何构造函数参数本身对被反序列化的对象都有一个"$ref",则这看起来并不容易修复。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36866131

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档