首页 > 解决方案 > Json.Net:将关系转换为引用并再次返回(one->many)

问题描述

我正在使用 .NET/ MyCouch将普通 POCO 对象读/写到 NoSQL 存储 (couchdb)。这很好用,也适用于具有简单关系的对象。

对于我正在从事的项目,我想将 NoSQL/MyCouch 中类型之间的关系表示为“ids”,并且我想了解更多关于如何充分利用 JSon.NET 的信息(MyCouch 使用json.net)。

例如:登录有很多用户

class Common {
   public string _id; 
   public string _rev;
   ... more here, including ID generation ...
} 

class Login : Common {
   public string Name {get;set;}
   public List<User> users {get;set;}
}

class User : Common {
   public string email {get;set};
}

保存用户时会生成一个嵌套的 JSON 文档:

{
   "name": "Neil"
   ...
   "users": [
      {
         "_id": "user:foo:something",
         "_rev": "32-92cca77ee07b8149b8d3dd76f8c8b08d"
         "email": "foo@there.com"
         ...
      },
      ...
   ]
}

在正常情况下,MyCouch(通过 Json.NET 序列化)会遍历关系并生成嵌套的 JSON。耶。

'Common' 是一个包含 ID 的基类,并将 ID 存储为对象类型的混合:代码:UUID(因此上面的 'user:foo:something' 示例)。

我宁愿为用户生成对象 ID,但仅限于这些类型的关系。并非对于从 Common 继承的每个对象(所有对象都这样做)。我很确定我可以为所有 Common 对象添加一个转换器,但认为这会破坏所有对象的基本序列化。我只想为实际的列表关系创建“ID 列表”。

我想像这样的 JSON 文档输出:

{
   name: "Neil"
   ...
   users: ["user:foo:something", ... ]
}

我可以通过自定义 Json.NET 序列化程序来做到这一点:

    [JsonConverterAttribute(typeof(ReferenceListConverter), typeof(User))]
    public List<User> Users { get; set; }

这非常适合输出。我得到了我想要的。这是 ReferenceListConverter:

public class ReferenceListConverter : JsonConverter
{
    private Type expectedType;

    public override bool CanRead => true;

    public override bool CanWrite => true;

    public ReferenceListConverter(Type expectedType)
    {
        this.expectedType = expectedType;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Common).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Get the _id, and _rev, and construct a real object from those
        // In this case, it means making another request to the server.
        var listType = typeof(List<>);
        var list = listType.MakeGenericType(this.expectedType);
        if (reader.TokenType != JsonToken.Null)
        {
            if (reader.TokenType == JsonToken.StartArray)
            {
                JToken token = JToken.Load(reader);
                List<string> items = token.ToObject<List<string>>();

                // Construct some items
                // ID's are of form <typename>:<id>
                MethodInfo program = typeof(Program).GetMethod("FetchObjectsFromStore", System.Reflection.BindingFlags.Static | BindingFlags.Public);
                MethodInfo generic = program.MakeGenericMethod(this.expectedType);

                string[] parameters = items.ToArray();
                var result = generic.Invoke(null, new object[] { parameters });
                return result;
            }
        }
        return list;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // Write out only the ID of the object
        JArray array = new JArray();
        if(value is IEnumerable<Common>)
        {
            foreach (Common item in (IEnumerable<Common>)value)
            {
                array.Add(JToken.FromObject(item._id));
            }
        }
        array.WriteTo(writer);
    }
}

1) 阅读……是有问题的。基本的。但它适用于一个简单的案例。

我无法通过 IoC 注入我的“db”类(我正在使用 SimpleInjector,但我不知道如何使用 IoC 为单个字段注入特定的 JsonConverter)。所以 ReferenceListConverter 使用静态方法来进行后续查找,即“meh”,但在我学习时还可以。

2)这不考虑周期。只要用户 <-> 登录,我就有一个无限循环。

我的问题是知识渊博的 Json.NET 人:有没有“更好的方法”来做到这一点?自定义 Json.NET 引用机制?其他?

标签: c#json.net

解决方案


推荐阅读