在.NET序列化大量关联数据(定制JSON.NET参考) [英] Serializing heavily linked data in .NET (customizing JSON.NET references)

查看:177
本文介绍了在.NET序列化大量关联数据(定制JSON.NET参考)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想避免序列数据时重新发明轮子。我知道一些方法来序列化它们相互联系的对象,但是它写一些code写了大量的code系列化的范围,我想避免这种情况。必须有一些通用的解决方案。

I want to avoid reinventing the wheel when serializing data. I know some ways to serialize objects which are linked to each other, but it ranges from writing some code to writing a lot of code for serialization, and I'd like to avoid that. There must be some generic solutions.

让我们说我有一个这样的结构:

Let's say I have a structure like this:

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 } };

我想将数据序列化到这样的事情:

I want to serialize data to something like this:

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或什么的。

Here, links are serialized as just names, with the assumption that names are unique. Links can also be "family.persons.0" or generated unique IDs or whatever.

要求:

  1. 格式必须为人类可读和preferably 人写的太。因此,为了对preference:JSON,YAML *,XML定制。没有二进制格式。

  1. Format must be human-readable and preferably human-writable too. So, in order of preference: JSON, YAML*, XML, custom. No binary formats.

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

Serialization must support all good stuff .NET offers. Generics are a must, including types like IEnumerable<>, IDictionary<> etc. Dynamic types / untyped objects are desirable.

格式的不得执行。没有Lua中,巨蟒等脚本之类的东西。

Format must not be executable. No Lua, Python etc. scripts and things like that.

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

If unique IDs are generated, they must be stable (persist through serialization-deserialization), as files will be put into a version control system.

*闻讯赶来YAML,但遗憾的是,它似乎是pretty的多死了。

* Heard about YAML, but sadly, it seems to be pretty much dead.

推荐答案

解决了使用JSON.NET问题(梦幻般的图书馆!)。现在的对象是,首先,系列化,准确地引用在这里我希望他们他们;和第二,没有众多的$ id和$ REF字段。在我的解决方案中,对象的第一属性用作它的标识符

Solved the problem using JSON.NET (fantastic library!). Now objects are, first, serialized and referenced exactly where I want them them to; and second, without numerous "$id" and "$ref" fields. In my solution, the first property of an object is used as its identifier.

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

I've created two JsonConvertors (for references to objects and for referenced objects):

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));
    }
}

和上下文保存引用的数据(与一个字典的每个类型,所以标识需要是唯一的,仅在相同类型的对象):

and a context to hold references data (with a dictionary for each type, so IDs need to be unique only among objects of the same type):

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;
    }
}

是必要的属性有些属性:

Some attributes on the properties are necessary:

[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;
}

所以,当我序列化和反序列化使用该code:

So, when I serialize and deserialize using this code:

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:

I get this neat 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 ,即使在技术上是不必要的。它可能产生相当多的对象,因此加载速度会变慢。不过貌似这是最广泛使用的方法,用于自定义对象的转换器。方法,它们可以用来避免这个是私人反正

What I don't like in my solution, is that I have to use JObject, even though technically it's unnecessary. It probably creates quite a bit of objects, so loading will be slower. But looks like this is the most widely used approach for customizing convertors of objects. Methods which could be used to avoid this are private anyway.

这篇关于在.NET序列化大量关联数据(定制JSON.NET参考)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆