JSON.NET (素晴らしいライブラリ!) を使用して問題を解決しました。これで、オブジェクトは最初にシリアル化され、必要な場所で正確に参照されます。第二に、多数の「$id」および「$ref」フィールドがありません。私のソリューションでは、オブジェクトの最初のプロパティがその識別子として使用されます。
2 つの を作成しましたJsonConvertor
(オブジェクトへの参照用と参照されるオブジェクト用):
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));
}
}
および参照データを保持するコンテキスト (各タイプの辞書を使用するため、ID は同じタイプのオブジェクト間でのみ一意である必要があります):
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;
}
}
プロパティのいくつかの属性が必要です:
[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, new JsonLinkedContext()),
});
私はこのきれいな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
、技術的には不要ですが、 を使用する必要があることです。おそらくかなりの数のオブジェクトが作成されるため、読み込みが遅くなります。しかし、これは、オブジェクトのコンバーターをカスタマイズするために最も広く使用されているアプローチのようです。これを回避するために使用できるメソッドは、とにかく非公開です。