Как сказал @Marc, проводной формат отправляет данные только для элементов, поэтому, чтобы узнать, был ли список пустым или нулевым, необходимо добавить этот бит информации в поток.
Добавить дополнительное свойство, чтобы указать, была ли исходная коллекция пустой или нет, легко, но если вы не хотите изменять определение исходного типа, у вас есть еще две опции:
Сериализация с использованием суррогата
Суррогатный тип будет иметь дополнительное свойство (при этом исходный тип не будет затронут) и будет восстанавливать исходное состояние списка: пустое, с элементами или пустым.
[TestMethod]
public void SerializeEmptyCollectionUsingSurrogate_RemainEmpty()
{
var instance = new SomeType { Items = new List<int>() };
// set the surrogate
RuntimeTypeModel.Default.Add(typeof(SomeType), true).SetSurrogate(typeof(SomeTypeSurrogate));
// serialize-deserialize using cloning
var clone = Serializer.DeepClone(instance);
// clone is not null and empty
Assert.IsNotNull(clone.Items);
Assert.AreEqual(0, clone.Items.Count);
}
[ProtoContract]
public class SomeType
{
[ProtoMember(1)]
public List<int> Items { get; set; }
}
[ProtoContract]
public class SomeTypeSurrogate
{
[ProtoMember(1)]
public List<int> Items { get; set; }
[ProtoMember(2)]
public bool ItemsIsEmpty { get; set; }
public static implicit operator SomeTypeSurrogate(SomeType value)
{
return value != null
? new SomeTypeSurrogate { Items = value.Items, ItemsIsEmpty = value.Items != null && value.Items.Count == 0 }
: null;
}
public static implicit operator SomeType(SomeTypeSurrogate value)
{
return value != null
? new SomeType { Items = value.ItemsIsEmpty ? new List<int>() : value.Items }
: null;
}
}
Сделайте ваши типы расширяемыми
protobuf-net предлагает интерфейс IExtensible, который позволяет расширять типы, чтобы поля можно было добавлять в сообщение без каких-либо нарушений (подробнее здесь ). Чтобы использовать расширение protobuf-net, вы можете унаследовать класс Extensible
или реализовать интерфейс IExtensible
, чтобы избежать ограничения наследования.
Теперь, когда ваш тип является «расширяемым», вы определяете методы [OnSerializing]
и [OnDeserialized]
, чтобы добавить новые индикаторы, которые будут сериализованы в поток и десериализованы из него при реконструкции объекта с его исходным состоянием.
Плюсы в том, что вам не нужно определять новые свойства или новые типы как суррогаты, минусы в том, что IExtensible
не поддерживается, если у вашего типа есть подтипы, определенные в вашей модели типов.
[TestMethod]
public void SerializeEmptyCollectionInExtensibleType_RemainEmpty()
{
var instance = new Store { Products = new List<string>() };
// serialize-deserialize using cloning
var clone = Serializer.DeepClone(instance);
// clone is not null and empty
Assert.IsNotNull(clone.Products);
Assert.AreEqual(0, clone.Products.Count);
}
[ProtoContract]
public class Store : Extensible
{
[ProtoMember(1)]
public List<string> Products { get; set; }
[OnSerializing]
public void OnDeserializing()
{
var productsListIsEmpty = this.Products != null && this.Products.Count == 0;
Extensible.AppendValue(this, 101, productsListIsEmpty);
}
[OnDeserialized]
public void OnDeserialized()
{
var productsListIsEmpty = Extensible.GetValue<bool>(this, 101);
if (productsListIsEmpty)
this.Products = new List<string>();
}
}