【问题标题】:Updating the name of a Json Property in a class更新类中 Json 属性的名称
【发布时间】:2021-12-07 12:13:14
【问题描述】:

我正在尝试更新已发布的 Json 可序列化类中的属性名称,因此我需要使其向后兼容。

public class myClass
{
    //[JsonIgnore] - now old json files can't be read, so this won't work...
    //[JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Error)] - didn't do anything
    //[JsonProperty(nameof(NewName)] - throws error - "That already exists"
    [Obselete("Use NewName instead")]
    public List<string> OldName { get => newName; set => newName = value; }

    public List<string> NewName { get; set; } = new List<string>();
}

我是这样使用它的:

[Test]
public void test()
{
    var foo = new myClass()
    {
        OldName = { "test" },
    };

    var bar = JsonConvert.SerializeObject(foo);
    var result = JsonConvert.DeserializeObject(bar, typeof(myClass));
}

当我查看 result.NewName 中的值时,我发现了这个列表:{"test", "test"},但我期望这个列表:{"test"}

期望的行为:

  • 如果有人已经在他们的代码中使用 OldName,他们会收到过时警告
  • 如果有人解析包含 OldName 的旧 json 文件,它会正确映射到 NewName
  • 创建的新 json 文件使用 NewName,而在 json 文件中的任何位置都找不到 OldName
  • 在任何情况下都不会将值反序列化两次并放入同一个列表中

你会如何做到这一点?

【问题讨论】:

  • 我会为这种类型创建自定义转换器。
  • 将 [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] 放在 OldName 和 NewName 上会阻止它附加到列表中,但不会阻止 Json 中的重复属性

标签: c# json parsing json.net


【解决方案1】:

试试这个

var foo = "{\"OldName\":[\"old test\"]}";
var fooN = "{\"NewName\":[\"new test\"]}";
var result = JsonConvert.DeserializeObject(foo, typeof(myClass));
//or
var result = JsonConvert.DeserializeObject(fooN, typeof(myClass));

var json = JsonConvert.SerializeObject(result);

json 结果:

{"NewName":["new test"]}
//or
{"NewName":["old test"]}

public class myClass
{
   [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public List<string> OldName {

        get {return null; }
        set {NewName=value;} 
    }

    public List<string> NewName {get; set;}
}
    

【讨论】:

  • 太棒了!不幸的是,就我而言,我现在仍然需要允许人们访问 OldName 属性。但我相信你的回答会帮助别人!感谢您的关注!
【解决方案2】:

这适用于 System.Text.Json。

            var foo = new myClass()
            {
                OldName = { "test" },
            };

            var bar = JsonSerializer.Serialize<myClass>(foo);
            var result = JsonSerializer.Deserialize<myClass>(bar);

【讨论】:

  • 但是 json 字符串包含两个值:{"OldName":["test","test2"],"NewName":["test","test2"]}。解串器可以对 json 数组使用“附加”模式。这可以解释当 OldName 被反序列化时,OldName 设置了 2 个值(以及 NewName),然后反序列化器读取 NewName 并将 json 中的 2 个值附加到来自 OldName 的 List 的已经 2 个值中。
  • 是的。我想知道是否将 [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] 放在属性上会阻止它们追加。
【解决方案3】:

这个answer 很有帮助。基本上你像这样重构你的代码:

public class ControlReportResult : TargetReportResult
{
    public List<string> NewName { get; set; } = new();

    [JsonIgnore] // this stops the serializer from paying attention to this property
    [Obsolete("Use NewName instead")]
    public List<string> OldName => { get => NewName; set => NewName = value; }
    
    [JsonProperty("OldName")] //This doesn't throw an error because the serializer was told to ignore the real OldName property
    private List<string> _jsonOldNameSetter { set => NewName = value; } // no getter, so we are never going to write OldName out.
}

一些注意事项:

  • _jsonOldNameSetter 是私有的,所以你的界面还是干净的
  • OldName 仍然可用,但标记为已过时
  • 代码将读取带有要么 OldName 或NewName 的json 文件,但只会写入带有NewName 的文件

这不应该发生,但是如果您仍然以某种方式最终得到一个带有 一个 OldName 和一个 NewName 的 json 文件,请在 OldName 和 NewName 上方添加属性 [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] - 这样他们会相互覆盖,而不是追加。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    相关资源
    最近更新 更多