gpt4 book ai didi

c# - 在 .NET 中序列化高度链接的数据(自定义 JSON.NET 引用)

转载 作者:太空狗 更新时间:2023-10-29 20:05:27 25 4
gpt4 key购买 nike

我想避免在序列化数据时重新发明轮子。我知道一些序列化相互链接的对象的方法,但它的范围从编写一些代码到编写大量用于序列化的代码,我想避免这种情况。一定有一些通用的解决方案。

假设我有这样的结构:

Person
bro = new Person { name = "bro", pos = new Pos { x = 1, y = 5 } },
sis = new Person { name = "sis", pos = new Pos { x = 2, y = 6 } },
mom = new Person { name = "mom", pos = new Pos { x = 3, y = 7 },
children = new List<Person> { bro, sis }
},
dad = new Person { name = "dad", pos = new Pos { x = 4, y = 8 },
children = new List<Person> { bro, sis }, mate = mom
};
mom.mate = dad;
Family family = new Family { persons = new List<Person> { mom, dad, bro, sis } };

我想将数据序列化为这样的东西:

family: {
persons: [
{ name: "bro", pos: { x: 1, y: 5 } },
{ name: "sis", pos: { x: 2, y: 6 } },
{ name: "mom", pos: { x: 3, y: 7 }, mate: "dad", children: [ "bro", "sis" ] },
{ name: "dad", pos: { x: 4, y: 8 }, mate: "mom", children: [ "bro", "sis" ] },
]
}

在这里,链接被序列化为名称,假设名称是唯一的。链接也可以是“family.persons.0”或生成的唯一 ID 或其他任何内容。

要求:

  1. 格式必须人类可读,最好也是人类可写。因此,按优先顺序排列:JSON、YAML*、XML、自定义。没有二进制格式。

  2. 序列化必须支持 .NET 提供的所有好东西。 泛型是必须的,包括 IEnumerable<>、IDictionary<> 等类型。动态类型/无类型对象是可取的。

  3. 格式不得可执行。没有 Lua、Python 等脚本之类的东西。

  4. 如果生成了唯一 ID,它们必须是稳定的(通过序列化-反序列化持续存在),因为文件将被放入版本控制系统

* 听说过 YAML,但遗憾的是,它似乎已经死了。

最佳答案

使用 JSON.NET(很棒的库!)解决了问题。现在,对象首先被序列化并准确地引用到我想要它们的位置;其次,没有大量的“$id”和“$ref”字段。在我的解决方案中,对象的第一个属性用作其标识符。

我创建了两个 JsonConvertor(用于引用对象和引用对象):

interface IJsonLinkable
{
string Id { get; }
}

class JsonRefConverter : JsonConverter
{
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((IJsonLinkable)value).Id);
}

public override object ReadJson (JsonReader reader, Type type, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.String)
throw new Exception("Ref value must be a string.");
return JsonLinkedContext.GetLinkedValue(serializer, type, reader.Value.ToString());
}

public override bool CanConvert (Type type)
{
return type.IsAssignableFrom(typeof(IJsonLinkable));
}
}

class JsonRefedConverter : JsonConverter
{
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}

public override object ReadJson (JsonReader reader, Type type, object existingValue, JsonSerializer serializer)
{
var jo = JObject.Load(reader);
var value = JsonLinkedContext.GetLinkedValue(serializer, type, (string)jo.PropertyValues().First());
serializer.Populate(jo.CreateReader(), value);
return value;
}

public override bool CanConvert (Type type)
{
return type.IsAssignableFrom(typeof(IJsonLinkable));
}
}

和一个保存引用数据的上下文(每个类型都有一个字典,因此 ID 只需要在相同类型的对象中是唯一的):

class JsonLinkedContext
{
private readonly IDictionary<Type, IDictionary<string, object>> links = new Dictionary<Type, IDictionary<string, object>>();

public static object GetLinkedValue (JsonSerializer serializer, Type type, string reference)
{
var context = (JsonLinkedContext)serializer.Context.Context;
IDictionary<string, object> links;
if (!context.links.TryGetValue(type, out links))
context.links[type] = links = new Dictionary<string, object>();
object value;
if (!links.TryGetValue(reference, out value))
links[reference] = value = FormatterServices.GetUninitializedObject(type);
return value;
}
}

属性上的一些属性是必需的:

[JsonObject(MemberSerialization.OptIn)]
class Family
{
[JsonProperty(ItemConverterType = typeof(JsonRefedConverter))]
public List<Person> persons;
}

[JsonObject(MemberSerialization.OptIn)]
class Person : IJsonLinkable
{
[JsonProperty]
public string name;
[JsonProperty]
public Pos pos;
[JsonProperty, JsonConverter(typeof(JsonRefConverter))]
public Person mate;
[JsonProperty(ItemConverterType = typeof(JsonRefConverter))]
public List<Person> children;

string IJsonLinkable.Id { get { return name; } }
}

[JsonObject(MemberSerialization.OptIn)]
class Pos
{
[JsonProperty]
public int x;
[JsonProperty]
public int y;
}

因此,当我使用这段代码进行序列化和反序列化时:

JsonConvert.SerializeObject(family, Formatting.Indented, new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
Context = new StreamingContext(StreamingContextStates.All, new JsonLinkedContext()),
});

JsonConvert.DeserializeObject<Family>(File.ReadAllText(@"..\..\Data\Family.json"), new JsonSerializerSettings {
Context = new StreamingContext(StreamingContextStates.All, new JsonLinkedContext()),
});

我得到了这个简洁的 JSON:

{
"persons": [
{
"name": "mom",
"pos": {
"x": 3,
"y": 7
},
"mate": "dad",
"children": [
"bro",
"sis"
]
},
{
"name": "dad",
"pos": {
"x": 4,
"y": 8
},
"mate": "mom",
"children": [
"bro",
"sis"
]
},
{
"name": "bro",
"pos": {
"x": 1,
"y": 5
}
},
{
"name": "sis",
"pos": {
"x": 2,
"y": 6
}
}
]
}

在我的解决方案中,我不喜欢的是我必须使用 JObject,尽管从技术上讲这是不必要的。它可能会创建相当多的对象,因此加载速度会变慢。但看起来这是用于自定义对象转换器的最广泛使用的方法。无论如何,可以用来避免这种情况的方法都是私有(private)的。

关于c# - 在 .NET 中序列化高度链接的数据(自定义 JSON.NET 引用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13108693/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com