- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我为 android 开发了一个应用程序,它将 JSON 文件中的序列化域模型存储到本地存储。现在的问题是,有时我会更改域模型(新功能)并希望可以选择 轻松从本地存储加载 JSON 文件的先前结构。我怎样才能做到这一点?
我想匿名反序列化对象并使用自动映射器,但我想在走这条路之前先听听别人的想法。
如果需要域模型的代码示例(之前和之后),我会提供。谢谢大家。
最佳答案
您如何支持向后兼容性取决于您的“之前”和“之后”模型将有多大不同。
如果您只是要添加新属性,那么这根本不会造成问题;您可以将旧的 JSON 反序列化为新模型,它可以正常工作而不会出错。
如果您用不同的属性替换过时的属性,您可以使用 Making a property deserialize but not serialize with json.net 中描述的技术。将旧属性迁移到新属性。
如果您要进行重大的结构更改,那么您可能希望为每个版本使用不同的类。序列化模型时,请确保 Version
属性(或其他一些可靠的标记)被写入 JSON。然后当需要反序列化时,您可以将 JSON 加载到 JToken
中。 , 检查 Version
属性,然后为 JToken
中的版本填充适当的模型.如果你愿意,你可以把这个逻辑封装成一个 JsonConverter
类(class)。
让我们来看看一些例子。假设我们正在编写一个应用程序,它保留一些关于人的信息。我们将从最简单的模型开始:Person
具有一个人名的单一属性的类。
public class Person // Version 1
{
public string Name { get; set; }
}
让我们创建一个人的“数据库”(我将在这里使用一个简单的列表)并将其序列化。
List<Person> people = new List<Person>
{
new Person { Name = "Joe Schmoe" }
};
string json = JsonConvert.SerializeObject(people);
Console.WriteLine(json);
这为我们提供了以下 JSON。
[{"Name":"Joe Schmoe"}]
fiddle :
https://dotnetfiddle.net/NTOnu2
Person
类看起来像新属性:
public class Person // Version 2
{
public string Name { get; set; }
public DateTime? Birthday { get; set; }
}
为了测试它,我们可以将版本 1 数据反序列化为这个新模型,然后向列表中添加一个新人并将模型序列化回 JSON。 (我还将添加一个格式选项以使 JSON 更易于阅读。)
List<Person> people = JsonConvert.DeserializeObject<List<Person>>(json);
people.Add(new Person { Name = "Jane Doe", Birthday = new DateTime(1988, 10, 6) });
json = JsonConvert.SerializeObject(people, Formatting.Indented);
Console.WriteLine(json);
一切都很好。这是 JSON 现在的样子:
[
{
"Name": "Joe Schmoe",
"Birthday": null
},
{
"Name": "Jane Doe",
"Birthday": "1988-10-06T00:00:00"
}
]
fiddle :
https://dotnetfiddle.net/pftGav
Name
属性不够强大。如果我们有单独的
FirstName
会更好和
LastName
属性代替。这样我们就可以做一些事情,比如按目录顺序(最后一个,第一个)对名字进行排序,并打印非正式的问候语,比如“嗨,乔!”。
Name
属性并从中填充两个新属性。在我们这样做之后,我们要处理
Name
过时的属性(property);我们不希望它在 future 写回 JSON。
FirstName
和
LastName
,我们需要改变旧的
Name
属性如下:
set
方法集FirstName
和 LastName
如上所述的属性; get
方法使Name
属性不会写入 JSON; Person
的公共(public)接口(interface)的一部分; [JsonProperty]
属性,以便 Json.Net 仍然可以“看到”它,即使它是私有(private)的。 Name
的代码。属性改为使用新属性。这是我们的
Person
类现在看起来像:
public class Person // Version 3
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Birthday { get; set; }
// This property is here to support transitioning from Version 2 to Version 3
[JsonProperty]
private string Name
{
set
{
if (value != null)
{
string[] parts = value.Trim().Split(' ');
if (parts.Length > 0) FirstName = parts[0];
if (parts.Length > 1) LastName = parts[1];
}
}
}
}
为了证明一切正常,让我们将版本 2 JSON 加载到此模型中,按姓氏对人员进行排序,然后将其重新序列化为 JSON:
List<Person> people = JsonConvert.DeserializeObject<List<Person>>(json);
people = people.OrderBy(p => p.LastName).ThenBy(p => p.FirstName).ToList();
json = JsonConvert.SerializeObject(people, Formatting.Indented);
Console.WriteLine(json);
看起来挺好的!结果如下:
[
{
"FirstName": "Jane",
"LastName": "Doe",
"Birthday": "1988-10-06T00:00:00"
},
{
"FirstName": "Joe",
"LastName": "Schmoe",
"Birthday": null
}
]
fiddle :
https://dotnetfiddle.net/T8NXMM
Address
类(class)类别:
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
我们可以重复使用相同的
Person
类(class);我们需要的唯一更改是添加
AddressId
属性将每个人链接到一个地址。
public class Person
{
public int? AddressId { get; set; }
...
}
最后,我们在根级别需要一个新类来保存人员和地址列表。我们也给它一个
Version
属性以防我们将来需要更改数据模型:
public class RootModel
{
public string Version { get { return "4"; } }
public List<Person> People { get; set; }
public List<Address> Addresses { get; set; }
}
模型就是这样;现在最大的问题是我们如何处理不同的 JSON?在版本 3 及更早版本中,JSON 是一个对象数组。但是对于这个新模型,JSON 将是一个包含两个数组的对象。
JsonConverter
对于新模型。我们可以将 JSON 读入
JToken
然后根据我们发现的内容(数组与对象)以不同方式填充新模型。如果我们得到一个对象,我们将检查我们刚刚添加到模型中的新版本号属性。
public class RootModelConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(RootModel);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
RootModel model = new RootModel();
if (token.Type == JTokenType.Array)
{
// we have a Version 3 or earlier model, which is just a list of people.
model.People = token.ToObject<List<Person>>(serializer);
model.Addresses = new List<Address>();
return model;
}
else if (token.Type == JTokenType.Object)
{
// Check that the version is something we are expecting
string version = (string)token["Version"];
if (version == "4")
{
// all good, so populate the current model
serializer.Populate(token.CreateReader(), model);
return model;
}
else
{
throw new JsonException("Unexpected version: " + version);
}
}
else
{
throw new JsonException("Unexpected token: " + token.Type);
}
}
// This signals that we just want to use the default serialization for writing
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
要使用转换器,我们创建一个实例并将其传递给
DeserializeObject
像这样的方法:
RootModelConverter converter = new RootModelConverter();
RootModel model = JsonConvert.DeserializeObject<RootModel>(json, converter);
现在我们已经加载了模型,我们可以更新数据以显示 Joe 和 Jane 住在同一地址并将其再次序列化:
model.Addresses.Add(new Address
{
Id = 1,
Street = "123 Main Street",
City = "Birmingham",
State = "AL",
PostalCode = "35201",
Country = "USA"
});
foreach (var person in model.People)
{
person.AddressId = 1;
}
json = JsonConvert.SerializeObject(model, Formatting.Indented);
Console.WriteLine(json);
这是生成的 JSON:
{
"Version": 4,
"People": [
{
"FirstName": "Jane",
"LastName": "Doe",
"Birthday": "1988-10-06T00:00:00",
"AddressId": 1
},
{
"FirstName": "Joe",
"LastName": "Schmoe",
"Birthday": null,
"AddressId": 1
}
],
"Addresses": [
{
"Id": 1,
"Street": "123 Main Street",
"City": "Birmingham",
"State": "AL",
"PostalCode": "35201",
"Country": "USA"
}
]
}
我们可以通过再次反序列化并转储一些数据来确认转换器也适用于新的第 4 版 JSON 格式:
model = JsonConvert.DeserializeObject<RootModel>(json, converter);
foreach (var person in model.People)
{
Address addr = model.Addresses.FirstOrDefault(a => a.Id == person.AddressId);
Console.Write(person.FirstName + " " + person.LastName);
Console.WriteLine(addr != null ? " lives in " + addr.City + ", " + addr.State : "");
}
输出:
Jane Doe lives in Birmingham, AL
Joe Schmoe lives in Birmingham, AL
fiddle :
https://dotnetfiddle.net/4lcDvE
关于c# - 为旧的 JSON 结构添加向后兼容性支持,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53391878/
我创建了一个用户可以添加测试的字段。这一切运行顺利我只希望当用户点击(添加另一个测试)然后上一个(添加另一个测试)删除并且这个显示在新字段中。 所有运行良好的唯一问题是点击(添加另一个字段)之前添加另
String[] option = {"Adlawan", "Angeles", "Arreza", "Benenoso", "Bermas", "Brebant
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我正在努力将 jQuery 滚动功能添加到 nav-tab (Bootstrap 3)。我希望用户能够选择他们想要的选项卡,并在选项卡内容中有一个可以平滑滚动到 anchor 的链接。这是我的代码,可
我正在尝试在用户登录后再添加 2 个 ui 选项卡。首先,我尝试做一个之后。 $('#slideshow').tabs('remove', '4'); $("#slideshow ul li:last
我有一个包含选择元素的表单,我想通过选择添加和删除其中一些元素。这是html代码(这里也有jsfiddle http://jsfiddle.net/txhajy2w/):
正在写这个: view.backgroundColor = UIColor.white.withAlphaComponent(0.9) 等同于: view.backgroundColor = UICo
好的,如果其中有任何信息,我想将这些列添加到一起。所以说我有 账户 1 2 3 . 有 4 个帐户空间,但只有 3 个帐户。我如何创建 java 脚本来添加它。 最佳答案 Live Example H
我想知道是否有一种有效的预制算法来确定一组数字的和/差是否可以等于不同的数字。示例: 5、8、10、2,使用 + 或 - 等于 9。5 - 8 = -3 + 10 = 7 + 2 = 9 如果有一个预
我似乎有一个卡住的 git repo。它卡在所有基本的添加、提交命令上,git push 返回所有内容为最新的。 从其他帖子我已经完成了 git gc 和 git fsck/ 我认为基本的调试步骤是
我的 Oracle SQL 查询如下- Q1- select hca.account_number, hca.attribute3, SUM(rcl.extended_amou
我正在阅读 http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingG
我正在尝试添加一个“加载更多”按钮并限制下面的结果,这样投资组合页面中就不会同时加载 1000 个内容,如下所示:http://typesetdesign.com/portfolio/ 我对 PHP
我遇到这个问题,我添加了 8 个文本框,它工作正常,但是当我添加更多文本框(如 16 个文本框)时,它不会添加最后一个文本框。有人遇到过这个问题吗?提前致谢。 Live Link: JAVASCRIP
add/remove clone first row default not delete 添加/删除克隆第一行默认不删除&并获取正确的SrNo(例如:添加3行并在看到问题后删除SrNo.2)
我编码this ,但删除按钮不起作用。我在控制台中没有任何错误.. var counter = 0; var dataList = document.getElementById('materi
我有一个类似数组的对象: [1:数组[10]、2:数组[2]、3:数组[2]、4:数组[2]、5:数组[3]、6:数组[1]] 我正在尝试删除前两个元素,执行一些操作,然后将它们再次插入到同一位置。
使用的 Delphi 版本:2007 你好, 我有一个 Tecord 数组 TInfo = Record Name : String; Price : Integer; end; var Info
我使用了基本的 gridster 代码,然后我声明了通过按钮添加和删除小部件的函数它工作正常但是当我将调整大小功能添加到上面的代码中时,它都不起作用(我的意思是调整大小,添加和删除小部件) 我的js代
title 323 323 323 title 323 323 323 title 323 323 323 JS $(document).keydown(function(e){
我是一名优秀的程序员,十分优秀!