带有私有列表的自定义集合中的序列化问题
Serialization problem in a custom collection with a private list
给定两个类如下:
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);
}
}
我在将 1,2,3 添加到它们中的每一个并使用 JsonConvert.SerializeObject 序列化后得到:
ListCollection: [1,2,3]
PrivateListCollection: {"items":[1,2,3]}
问题是是否可以得到PrivateListCollection的序列化结果为[1,2,3](没有前面的“items”之类的)?
POCO 无法通过实现 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:
将自身序列化为 JSON 数组
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.
要将 PrivateListCollection
序列化为 JSON 数组,您有两种选择。
首先,你可以创建一个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);
}
}
然后像这样将它添加到 PrivateListCollection
:
[Serializable]
[JsonConverter(typeof(PrivateListCollectionConverter))]
class PrivateListCollection: ISerializable
{
// Remainder unchanged
或像这样将其添加到 JsonSerializerSettings
:
var settings = new JsonSerializerSettings
{
Converters = { new PrivateListCollectionConverter() },
};
演示 fiddle #1 here.
其次,你可以让 PrivateListCollection
实现 IEnumerable<int>
并添加一个参数化的构造函数采用单个 IEnumerable<int>
参数,如下所示:
[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();
}
这样做后,Json.NET 会将 PrivateListCollection
视为一个 constructible read-only collection,它可以作为一个 JSON 数组往返。但是,基于你已经命名你的类型PrivateListCollection
,你可能不想实现枚举。
演示 fiddle #2 here.
给定两个类如下:
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);
}
}
我在将 1,2,3 添加到它们中的每一个并使用 JsonConvert.SerializeObject 序列化后得到:
ListCollection: [1,2,3]
PrivateListCollection: {"items":[1,2,3]}
问题是是否可以得到PrivateListCollection的序列化结果为[1,2,3](没有前面的“items”之类的)?
POCO 无法通过实现 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.
要将 PrivateListCollection
序列化为 JSON 数组,您有两种选择。
首先,你可以创建一个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);
}
}
然后像这样将它添加到 PrivateListCollection
:
[Serializable]
[JsonConverter(typeof(PrivateListCollectionConverter))]
class PrivateListCollection: ISerializable
{
// Remainder unchanged
或像这样将其添加到 JsonSerializerSettings
:
var settings = new JsonSerializerSettings
{
Converters = { new PrivateListCollectionConverter() },
};
演示 fiddle #1 here.
其次,你可以让 PrivateListCollection
实现 IEnumerable<int>
并添加一个参数化的构造函数采用单个 IEnumerable<int>
参数,如下所示:
[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();
}
这样做后,Json.NET 会将 PrivateListCollection
视为一个 constructible read-only collection,它可以作为一个 JSON 数组往返。但是,基于你已经命名你的类型PrivateListCollection
,你可能不想实现枚举。
演示 fiddle #2 here.