如何使用Json.NET作为对象并嵌套对象作为引用来序列化顶级对象

时间:2019-03-29 17:22:22

标签: serialization json.net

如果对象位于序列化上下文的顶层,我希望能够完全序列化该对象,但是可以通过引用将上下文中较低的对象序列化。

我已经使用自定义合同解析器,自定义Json转换器和自定义IReferenceResolver搜索并尝试了测试,但找不到解决方法。

例如,想象一下一个IdType类,在顶层我想对其所有属性进行序列化,但是在属性,列表或字典中遇到这样的对象的引用时,我想产生一个引用。

对此类型进行测试

public class IdType
{
   public IdType(string id)
   {
      Id = id;
   }

   public string Id {get;}

   public string Name {get;set;}

   public int Number {get; set;} 

   public IdType OtherType { get; set; }

   public IEnumerable<IdType> Types { get; set;}

   public IDictionary<IdType, string> { get; set; }

   public IDictionary<string, IdType> {get; set; }
}

[TestMethod]
public void SerializeTest()
{
    var t1 = new IdType(1) { Name = 'Alice', Number = 42 };
    var t2 = new IdType(2) { Name = 'Bob', Number = 21, OtherType = t1 };
    var t3 = new IdType(2) { Name = 'Charlie', Number = 84, OtherType = t2, Types = new[] {t1, t2} };

    var testTypes = new[] 
    {
        t1,
        t3
    };

    var serializer = new JsonSerializer
    {
        Formatting = Formatting.Indented,
    };

    StringWriter writer;
    using (writer = new StringWriter())
    {
        serializer.Serialize(writer, myObject);
    }

    Console.WriteLine(writer.ToString());    
}

我想要这样的输出


[
    {
      "Id": "1",
      "Name": "Alice"
      "Number": 42,
    },
    {
      "Id": "3",
      "Name": "Charlie"
      "Number": 84,
      "OtherType": 2
      "Types": [
         "Id" :  1, 2
      ]
    }
]

JsonConverter没有上下文,因此它将始终以一种方式或另一种方式进行转换。

自定义解析器(从DefaultContractResolver派生)将适用于IdType类型的属性,但我不知道如何使其与列表和词典一起使用。

最近我尝试使用PreserveReferenceHandling和具有顶级元素ID的自定义IReferenceResolver。但这不起作用,因为序列化是深度优先。

我们将很高兴收到任何实现此目标的建议

1 个答案:

答案 0 :(得分:0)

我想我已经回答了我自己的问题。如果我结合使用了自定义合同解析器和自定义转换器,并有条件地将转换器添加到要序列化为ID的属性中,那么它似乎可以正常工作。

我还没有实现字典,但这适用于基本属性和列表:

public class CustomResolver : DefaultContractResolver
{
    readonly CamelCasePropertyNamesContractResolver innerResolver = new CamelCasePropertyNamesContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var jsonProperty = base.CreateProperty(member, memberSerialization);

        if (!jsonProperty.PropertyType.IsPrimitive && jsonProperty.PropertyType != typeof(string) && jsonProperty.Readable)
        {
            var innerContract = innerResolver.ResolveContract(jsonProperty.PropertyType);
            if (typeof(IdType).IsAssignableFrom(innerContract.UnderlyingType) && innerContract is JsonObjectContract objectContract)
            {
                jsonProperty.Converter = new IdConverter();
            }
            else if (typeof(IEnumerable<IdType>).IsAssignableFrom(innerContract.UnderlyingType) && innerContract is JsonArrayContract arrayContract)
            {
                jsonProperty.Converter = new IdConverter();
            }
        }

        return jsonProperty;
    }
}

internal class IdConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (typeof(IdType).IsAssignableFrom(objectType) ||
                typeof(IEnumerable<IdType>).IsAssignableFrom(objectType));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is IdType item)
        {
            JToken token = JToken.FromObject(item.Id);
            token.WriteTo(writer);
        }
        else if (value is IEnumerable<IdType> itemCollection)
        {
            JArray array = new JArray();
            foreach (var i in itemCollection)
            {
                JToken token = JToken.FromObject(i.Id);
                array.Add(token);
            }
            array.WriteTo(writer);
        }
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

要序列化并使用自定义解析器,您将:


    var serializer = new JsonSerializer
    {
        Formatting = Formatting.Indented,
        ContractResolver = new CustomResolver(),
    };