- 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/
我目前正在尝试基于哈希表构建字典。逻辑是:有一个名为 HashTable 的结构,其中包含以下内容: HashFunc HashFunc; PrintFunc PrintEntry; CompareF
如果我有一个指向结构/对象的指针,并且该结构/对象包含另外两个指向其他对象的指针,并且我想删除“包含这两个指针的对象而不破坏它所持有的指针”——我该怎么做这样做吗? 指向对象 A 的指针(包含指向对象
像这样的代码 package main import "fmt" type Hello struct { ID int Raw string } type World []*Hell
我有一个采用以下格式的 CSV: Module, Topic, Sub-topic 它需要能够导入到具有以下格式的 MySQL 数据库中: CREATE TABLE `modules` ( `id
通常我使用类似的东西 copy((uint8_t*)&POD, (uint8_t*)(&POD + 1 ), back_inserter(rawData)); copy((uint8_t*)&PODV
错误 : 联合只能在具有兼容列类型的表上执行。 结构(层:字符串,skyward_number:字符串,skyward_points:字符串)<> 结构(skyward_number:字符串,层:字符
我有一个指向结构的指针数组,我正在尝试使用它们进行 while 循环。我对如何准确初始化它并不完全有信心,但我一直这样做: Entry *newEntry = malloc(sizeof(Entry)
我正在学习 C,我的问题可能很愚蠢,但我很困惑。在这样的函数中: int afunction(somevariables) { if (someconditions)
我现在正在做一项编程作业,我并没有真正完全掌握链接,因为我们还没有涉及它。但是我觉得我需要它来做我想做的事情,因为数组还不够 我创建了一个结构,如下 struct node { float coef;
给定以下代码片段: #include #include #define MAX_SIZE 15 typedef struct{ int touchdowns; int intercepti
struct contact list[3]; int checknullarray() { for(int x=0;x<10;x++) { if(strlen(con
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Empty “for” loop in Facebook ajax what does AJAX call
我刚刚在反射器中浏览了一个文件,并在结构构造函数中看到了这个: this = new Binder.SyntaxNodeOrToken(); 我以前从未见过该术语。有人能解释一下这个赋值在 C# 中的
我经常使用字符串常量,例如: DICT_KEY1 = 'DICT_KEY1' DICT_KEY2 = 'DICT_KEY2' ... 很多时候我不介意实际的文字是什么,只要它们是独一无二的并且对人类读
我是 C 的新手,我不明白为什么下面的代码不起作用: typedef struct{ uint8_t a; uint8_t* b; } test_struct; test_struct
您能否制作一个行为类似于内置类之一的结构,您可以在其中直接分配值而无需调用属性? 前任: RoundedDouble count; count = 5; 而不是使用 RoundedDouble cou
这是我的代码: #include typedef struct { const char *description; float value; int age; } swag
在创建嵌套列表时,我认为 R 具有对列表元素有用的命名结构。我有一个列表列表,并希望应用包含在任何列表中的每个向量的函数。 lapply这样做但随后剥离了列表的命名结构。我该怎么办 lapply嵌套列
我正在做一个用于学习目的的个人组织者,我从来没有使用过 XML,所以我不确定我的解决方案是否是最好的。这是我附带的 XML 文件的基本结构:
我是新来的 nosql概念,所以当我开始学习时 PouchDB ,我找到了这个转换表。我的困惑是,如何PouchDB如果可以说我有多个表,是否意味着我需要创建多个数据库?因为根据我在 pouchdb
我是一名优秀的程序员,十分优秀!