Given two classes as follows:
class ListCollection : List<int>
{
}
[Serializable]
class PrivateListCollection: ISerializable
{
private List<int> list = new List<int>();
public void Add(int value)
{
list.Add(value);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("items", list);
}
}
I get after adding 1,2,3 to each of them and serializing using JsonConvert.SerializeObject this:
ListCollection: [1,2,3]
PrivateListCollection: {"items":[1,2,3]}
The question is whether it's possible to get the serialization result of PrivateListCollection as [1,2,3] (without the "items" or whatever in front of it)?
An POCO cannot serialize itself as a JSON array by implementing ISerializable
because a JSON array is an ordered sequence of values, while SerializationInfo
represents a collection of name/value pairs -- which matches exactly the definition of a JSON object rather than an array. This is confirmed by the Json.NET docs:
ISerializable
Types that implement ISerializable and are marked with SerializableAttribute are serialized as JSON objects. When serializing, only the values returned from ISerializable.GetObjectData are used; members on the type are ignored. When deserializing, the constructor with a SerializationInfo and StreamingContext is called, passing the JSON object's values.
To serialize your PrivateListCollection
as a JSON array, you have a couple of options.
Firstly, you could create a custom JsonConverer
:
class PrivateListCollectionConverter : JsonConverter<PrivateListCollection>
{
const string itemsName = "items";
public override PrivateListCollection ReadJson(JsonReader reader, Type objectType, PrivateListCollection existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var list = serializer.Deserialize<List<int>>(reader);
if (list == null)
return null;
existingValue = existingValue ?? new PrivateListCollection();
foreach (var item in list)
existingValue.Add(item);
return existingValue;
}
public override void WriteJson(JsonWriter writer, PrivateListCollection value, JsonSerializer serializer)
{
// There doesn't seem to be any way to extract the items from the PrivateListCollection other than to call GetObjectData(), so let's do that.
// Adding PrivateListCollectionConverter as a nested type inside PrivateListCollection would be another option to make the items accessible.
ISerializable serializable = value;
var info = new SerializationInfo(value.GetType(), new FormatterConverter());
serializable.GetObjectData(info, serializer.Context);
var list = info.GetValue(itemsName, typeof(IEnumerable<int>));
serializer.Serialize(writer, list);
}
}
And then either add it to PrivateListCollection
like so:
[Serializable]
[JsonConverter(typeof(PrivateListCollectionConverter))]
class PrivateListCollection: ISerializable
{
// Remainder unchanged
Or add it to JsonSerializerSettings
like so:
var settings = new JsonSerializerSettings
{
Converters = { new PrivateListCollectionConverter() },
};
Demo fiddle #1 here.
Secondly, you could make PrivateListCollection
implement IEnumerable<int>
and add a parameterized constructor taking a single IEnumerable<int>
parameter like so:
[Serializable]
class PrivateListCollection: ISerializable, IEnumerable<int>
{
public PrivateListCollection() { }
public PrivateListCollection(IEnumerable<int> items) => list.AddRange(items);
private List<int> list = new List<int>();
public void Add(int value)
{
list.Add(value);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("items", list);
}
public IEnumerator<int> GetEnumerator() => list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Having done so, Json.NET will see PrivateListCollection
as a constructible read-only collection which can be round-tripped as a JSON array. However, based on the fact that you have named your type PrivateListCollection
, you may not want to implement enumeration.
Demo fiddle #2 here.