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



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

我已经搜索并尝试了使用自定义合同解析器、自定义 JSON 转换器和自定义 IReference解析器进行测试,但我找不到执行此操作的方法。

例如,假设一个 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。但这不起作用,因为序列化首先是深度。

任何实现这一目标的建议将不胜感激

我想我已经回答了我自己的问题。如果我使用自定义协定解析器和自定义转换器的组合,并有条件地将转换器添加到我要序列化为 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(),
};

最新更新