【问题标题】:Json.Net calls property getter during deserialization of list, resulting in duplicate itemsJson.Net在列表反序列化过程中调用属性getter,导致重复项
【发布时间】:2014-08-28 16:15:56
【问题描述】:

我正在使用 json.net 为 winform 应用程序实现 memento pattern。我正在使用备忘录回滚失败的数据库事务中的对象。我遇到的问题是,在反序列化备忘录时,调用的是 getter 而不是 setter。让我演示一下:

class MyClass
{
    public int ID { get; set; }
    public string field1 { get; set; }
    public string field2 { get; set; }

    private List<SomeObject> _someObjects;
    public List<SomeObject> SomeObjects
    {
        get
        {
            if(_someObjects == null)
            {
                _someObjects = LoadSomeObjectsFromDB();
            }
            return _someObjects;
        }
        set
        {
            _someObjects = value;
        }
    }

    private List<AnotherObject> _anotherObjects;
    public List<AnotherObject> AnotherObjects
    {
        get
        {
            if(_anotherObjects == null)
            {
                _anotherObjects= LoadAnotherObjectsFromDB();
            }
            return _anotherObjects ;
        }
        set
        {
            _anotherObjects = value;
        }
    }
}

*MyObjectSomeObjectAnotherObject 扩展同一个基类 Model

从上面的示例类可以看出,如果 SomeObjects 还没有加载,它会使用Lazy Loading 从数据库中加载它。现在当我序列化这个对象时。

string memento = JsonConvert.SerializeObject(obj);

导致

{
  "ID": 1,
  "field1": "field 1",
  "field2": "field 2",
  "SomeObjects": [
    {
      "ID": 1,
    },
    {
      "ID": 2,
    },
    {
      "ID": 3,
    }
  ],
  "AnotherObjects": [
    {
      "ID": 4,
    },
    {
      "ID": 5,
    },
    {
      "ID": 6,
    }
  ]
}

然后反序列化它。

MyObject obj = JsonConvert.DeserializeObject(memento, typeof(MyObject));

我得到一个由以下 JSON 表示的对象

{
  "ID": 1,
  "field1": "field 1",
  "field2": "field 2",
  "SomeObjects": [
    {
      "ID": 1,
    },
    {
      "ID": 2,
    },
    {
      "ID": 3,
    },
    {
      "ID": 1,
    },
    {
      "ID": 2,
    },
    {
      "ID": 3,
    }
  ],
  "AnotherObjects": [
    {
      "ID": 4,
    },
    {
      "ID": 5,
    },
    {
      "ID": 6,
    },
    {
      "ID": 4,
    },
    {
      "ID": 5,
    },
    {
      "ID": 6,
    }
  ]
}

SomeObjectsAnotherObjects Lists 中的项目太多

SomeObjects getter 被调用,这会导致 List 从 DB 初始化,然后将序列化列表附加到它。让我在列表中留下比预期更多的项目。但是如果我删除延迟加载部分

public List<SomeObject> SomeObjects
{
    get
    {
        /*if(_someObjects == null)
        {
            _someObjects = LoadSomeObjectsFromDB();
        }*/
        return _someObjects;
    }
    set
    {
        _someObjects = value;
    }
}

getter 仍然被调用,但返回 null。然后 json.net 调用 setter,value 是一个已经添加了正确数量的元素的列表,而如果 getter 返回一个初始化列表,它会附加到它永远不会调用 ths setter。为什么存在差异,如果列表已经初始化,则调用 getter 并将其附加到。但如果未初始化,则调用 setter 并使用已填充对象的列表对其进行初始化。有没有办法让 json.net 只在通用List 反序列化期间调用设置器?

【问题讨论】:

    标签: c# winforms json.net json-deserialization


    【解决方案1】:

    Json.Net 有一个 ObjectCreationHandling 用于此目的的设置。尝试将其设置为Replace,例如:

    JsonSerializerSettings settings = new JsonSerializerSettings();
    settings.ObjectCreationHandling = ObjectCreationHandling.Replace;
    
    MyObject obj = JsonConvert.DeserializeObject<MyObject>(memento, settings);
    

    演示:https://dotnetfiddle.net/rkY1pt

    【讨论】:

    • 啊该死,我不觉得自己很笨吗。
    • @AndrewWhitaker 嘿,别出汗;如果您不了解设置,那么转换器实际上并不是一个糟糕的解决方案。
    最近更新 更多