为什么 Newtonsoft 反序列化器不反序列化智能 getter?

Ele*_*ley 5 c# json.net

语境

我有两个班级:

  • LiteralModel 是字符串 Value 属性的包装器。
  • Expression 有一个字符串 Name 和一个名为 Literal 的 LiteralModel。

    public class ExpressionModel
    {
        private string name;
        public string Name { get => name ?? ""; set => name = value; }
    
        private LiteralModel literal;
        public LiteralModel Literal
        {
            get => literal ?? new LiteralModel();
            set => literal = value;
        }
    }
    
    public class LiteralModel 
    {
        private string value;
        public string Value { get => value ?? ""; set => this.value = value; }
    }
    
    Run Code Online (Sandbox Code Playgroud)

所有都是具有公共 getter 和 setter 的公共属性,因此我希望它们都可以轻松序列化和反序列化,即使使用空值守卫也是如此,而且在大多数情况下,它们确实如此。

问题

ExpressionModel 的 Literal 属性没有正确反序列化。以下是演示该问题的最小测试:

    public void TestNewtonsoftExpressionDeserialization()
    {
        ExpressionModel expression = new ExpressionModel
        {
            Name = "test",
            Literal = new LiteralModel { Value = "61" }
        };

        string json = JsonConvert.SerializeObject(expression);

        Assert.IsTrue(json.Contains("61")); // passes

        ExpressionModel sut = JsonConvert.DeserializeObject<ExpressionModel>(json);

        Assert.AreEqual("test", sut.Name); // passes
        Assert.AreEqual("61", sut.Literal.Value); // fails
    }
Run Code Online (Sandbox Code Playgroud)

如您所见,JSON 看起来像我想要/期望的那样(包装字符串“61”),但是当我将其反序列化回 ExpressionModel 时,Literal 测试失败——它得到一个空字符串的 LiteralModel。

我试过的

如果我删除表达式模型的 Literal getter 的智能,它会按预期运行——所有测试都通过。但是智能属性确实适用于字符串属性。那么为什么不在我的 LiteralModel 对象上呢?

更奇怪的是,如果我将空检查移动到 setter 而不是 getter,则所有测试都会通过,如下所示:

        public LiteralModel Literal
        {
            get => literal;
            set => literal = value ?? new LiteralModel();
        }
Run Code Online (Sandbox Code Playgroud)

结论

简而言之,序列化程序和智能 setter 没有任何阶段,但智能 getter 会破坏反序列化,除了string.

这似乎是非常随意的行为。有谁知道为什么会这样,或者是否有任何方法可以让这些类按编写的方式工作?

Jon*_*eet 4

怀疑问题在于 Json.NET 正在调用您的属性 getter,然后对结果调用 setter。例如,类似这样的东西 - 尽管显然是通过反射:

var expression = new ExpressionModel();
expression.Name = "test";
var literal = expression.Literal;
if (literal is null)
{
    // No literal - create one and set it
    literal = new LiteralModel();
    expression.Literal = literal;
}
// Now literal is non-null either way, so set the value.
literal.Value = "61";
Run Code Online (Sandbox Code Playgroud)

根据您的代码的工作方式,您的Literalgetter 正在创建一个新的LiteralModel,但随后将其丢弃。撇开 Json.NET 不谈,这仍然很令人困惑。例如:

var expression = new Expression();
expression.Literal.Value = "foo";
Console.WriteLine(expression.Literal.Value); // Empty string
Run Code Online (Sandbox Code Playgroud)

如果创建了一个属性,您可以更改ExpressionModel代码以将新创建​​的属性分配给该属性:LiteralModel

var expression = new ExpressionModel();
expression.Name = "test";
var literal = expression.Literal;
if (literal is null)
{
    // No literal - create one and set it
    literal = new LiteralModel();
    expression.Literal = literal;
}
// Now literal is non-null either way, so set the value.
literal.Value = "61";
Run Code Online (Sandbox Code Playgroud)

这将避免“简单代码”行为如此令人困惑 - 我希望它也能修复 Json.NET 行为。