【问题标题】:Why does the Newtonsoft deserializer not deserialize a smart getter?为什么 Newtonsoft 反序列化器不反序列化智能 getter?
【发布时间】:2020-01-30 16:41:47
【问题描述】:

上下文

我有两个班级:

  • 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; }
    }
    

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

问题

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
    }

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

我的尝试

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

更奇怪的是,如果我将 null-check 移至 setter 而不是 getter,则所有测试都会通过:

        public LiteralModel Literal
        {
            get => literal;
            set => literal = value ?? new LiteralModel();
        }

结论

简而言之,序列化器和智能设置器没有任何阶段,但智能获取器会破坏反序列化,string 除外。

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

【问题讨论】:

标签: c# json.net


【解决方案1】:

我怀疑问题是 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";

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

var expression = new Expression();
expression.Literal.Value = "foo";
Console.WriteLine(expression.Literal.Value); // Empty string

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

public LiteralModel Literal
{
    get => literal ?? (literal = new LiteralModel());
    set => literal = value;
}

这将避免“简单代码”行为如此混乱 - 我预计它也将修复 Json.NET 行为。

【讨论】:

    猜你喜欢
    • 2020-03-03
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    相关资源
    最近更新 更多