以下に示すようないくつかのカスタムプロパティを持つカスタムコレクション(IListを実装)があります。
class FooCollection : IList<Foo> {
private List<Foo> _foos = new List<Foo>();
public string Bar { get; set; }
//Implement IList, ICollection and IEnumerable members...
}
シリアル化するときは、次のコードを使用します。
JsonSerializerSettings jss = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.Auto
};
string serializedCollection = JsonConvert.SerializeObject( value , jss );
すべてのコレクションアイテムを適切にシリアル化および逆シリアル化します。ただし、FooCollection
クラスの追加のプロパティは考慮されません。
とにかくそれらをシリアル化に含めることはありますか?
問題は次のとおりです。オブジェクトがIEnumerable
を実装すると、JSON.netはそれを値の配列として識別し、配列に従ってシリアル化します Json構文 (プロパティは含まれません)、例えば:
[ {"FooProperty" : 123}, {"FooProperty" : 456}, {"FooProperty" : 789}]
プロパティを保持してシリアル化する場合は、カスタムJsonConverter
を定義して、そのオブジェクトのシリアル化を手動で処理する必要があります。
// intermediate class that can be serialized by JSON.net
// and contains the same data as FooCollection
class FooCollectionSurrogate
{
// the collection of foo elements
public List<Foo> Collection { get; set; }
// the properties of FooCollection to serialize
public string Bar { get; set; }
}
public class FooCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(FooCollection);
}
public override object ReadJson(
JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
// N.B. null handling is missing
var surrogate = serializer.Deserialize<FooCollectionSurrogate>(reader);
var fooElements = surrogate.Collection;
var fooColl = new FooCollection { Bar = surrogate.Bar };
foreach (var el in fooElements)
fooColl.Add(el);
return fooColl;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
// N.B. null handling is missing
var fooColl = (FooCollection)value;
// create the surrogate and serialize it instead
// of the collection itself
var surrogate = new FooCollectionSurrogate()
{
Collection = fooColl.ToList(),
Bar = fooColl.Bar
};
serializer.Serialize(writer, surrogate);
}
}
次に、次のように使用します。
var ss = JsonConvert.SerializeObject(collection, new FooCollectionConverter());
var obj = JsonConvert.DeserializeObject<FooCollection>(ss, new FooCollectionConverter());
個人的には、可能な限りカスタムJsonConverter
sを作成することを避け、代わりにこの目的のために設計されたさまざまなJSON属性を利用するのが好きです。 FooCollection
を JsonObjectAttribute
で装飾するだけで、配列ではなくJSONオブジェクトとしてシリアル化を強制できます。 Count
プロパティとIsReadOnly
プロパティをJsonIgnore
で装飾して、出力に表示されないようにする必要があります。 _foos
をプライベートフィールドにしておきたい場合は、JsonProperty
で装飾する必要もあります。
[JsonObject]
class FooCollection : IList<Foo> {
[JsonProperty]
private List<Foo> _foos = new List<Foo>();
public string Bar { get; set; }
// IList implementation
[JsonIgnore]
public int Count { ... }
[JsonIgnore]
public bool IsReadOnly { ... }
}
シリアル化すると、次のような結果になります。
{
"_foos": [
"foo1",
"foo2"
],
"Bar": "bar"
}
明らかに、これは、これらの属性を追加するためにFooCollection
の定義を変更できる場合にのみ機能します。それ以外の場合は、カスタムコンバーターを使用する必要があります。
カスタムJsonConverter
を記述したくない場合、またはJSON属性( JsonObjectAttribute )を使用したくない場合は、次の拡張メソッドを使用できます。
public static string ToFooJson<T>(this FooCollection fooCollection)
{
return JsonConvert.SerializeObject(new
{
Bar = fooCollection.Bar,
Collection = fooCollection
});
}
リストまたはコレクション自体のコンテンツも保持したい場合は、プロパティを公開してリストを返すことを検討できます。シリアル化中の周期的な問題を防ぐために、ラッピングする必要があります。
_[JsonObject]
public class FooCollection : List<int>
{
[DataMember]
public string Bar { get; set; } = "Bar";
public ICollection<int> Items => new _<int>(this);
}
public class _<T> : ICollection<T>
{
public _(ICollection<T> collection) => Inner = collection;
public ICollection<T> Inner { get; }
public int Count => this.Inner.Count;
public bool IsReadOnly => this.Inner.IsReadOnly;
public void Add(T item) => this.Inner.Add(item);
public void Clear() => this.Inner.Clear();
public bool Contains(T item) => this.Inner.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => this.Inner.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator()=> this.Inner.GetEnumerator();
public bool Remove(T item) => this.Inner.Remove(item);
IEnumerator IEnumerable.GetEnumerator() => this.Inner.GetEnumerator();
}
_
_new FooCollection { 1, 2, 3, 4, 4 }
_ =>
_{
"Bar": "Bar",
"Items": [
1,
2,
3
],
"Capacity": 4,
"Count": 3
}
_
new FooCollection { 1, 2, 3 }.ToArray()
=> new []{1, 2, 3}.ToArray()
リストからの継承は機能しますか?
class FooCollection : List<Foo>, IList<Foo>
{
public string Bar { get; set; }
//Implement IList, ICollection and IEnumerable members...
}