更新类中 Json 属性的名称

Posted

技术标签:

【中文标题】更新类中 Json 属性的名称【英文标题】:Updating the name of a Json Property in a class 【发布时间】: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 中的重复属性 【参考方案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)] - 这样他们会相互覆盖,而不是追加。

【讨论】:

以上是关于更新类中 Json 属性的名称的主要内容,如果未能解决你的问题,请参考以下文章

序列化和反序列化过程中 JSON 属性的不同名称

从 json 文件中获取标签名称作为类中的变量

如何从父类中的名称动态设置属性?

如何在 TypeScript 类中正确声明计算属性名称?

休眠映射 - 在类中找不到属性名称的设置器

反射之取类中类的属性变量名称及其值