微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 在.NET中序列化大量链接的数据(自定义JSON.NET引用)

我希望在序列化数据时避免重新发明轮子.我知道一些方法来序列化彼此链接的对象,但它的范围从编写一些代码到编写大量代码进行序列化,我想避免这种情况.必须有一些通用的解决方案.

假设我有这样的结构:

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 },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",]
}

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

要求:

>格式必须是人类可读的,最好是人类可写的.因此,按优先顺序:JSON,YAML *,XML,自定义.没有二进制格式.
>序列化必须支持.NET提供的所有好东西.泛型是必须的,包括IEnumerable<>,IDictionary<>等类型.等等.动态类型/无类型对象是理想的.
>格式不能是可执行的.没有Lua,Python等脚本和类似的东西.
>如果生成唯一ID,它们必须是稳定的(通过序列化 – 反序列化保持),因为文件将被放入版本控制系统.

*听说过YAML,可悲的是,它似乎已经死了.

解决方法

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

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

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,JsonSerializer serializer)
    {
        serializer.Serialize(writer,value);
    }

    public override object ReadJson (JsonReader reader,JsonSerializer serializer)
    {
        var jo = JObject.Load(reader);
        var value = JsonLinkedContext.GetLinkedValue(serializer,(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,object>>();

    public static object GetLinkedValue (JsonSerializer serializer,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,});

我得到这个整洁的JSON:

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

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

原文地址:https://www.jb51.cc/csharp/100690.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐