Вот мой класс:
public class Command
{
[XmlArray(IsNullable = true)]
public List<Parameter> To { get; set; }
}
Когда я сериализирую объект этого класса:
var s = new XmlSerializer(typeof(Command));
s.Serialize(Console.Out, new Command());
это печатает как ожидалось (xml заголовок, и пространства имен MS по умолчанию опущены):
<Command><To xsi:nil="true" /></Command>
Когда я взял этот xml и попытался десериализовать его, я получил stucked, потому что он всегда печатает "Не пустой":
var t = s.Deserialize(...);
if (t.To == null)
Console.WriteLine("Null");
else
Console.WriteLine("Not null");
Как вынудить deserializer сделать мой пустой указатель списка, если это является пустым в xml?
Ух, раздражает, правда? Вы можете увидеть это, запустив sgen.exe в вашей сборке с параметрами / keep и / debug, чтобы вы могли отлаживать код десериализации. Это выглядит примерно так:
global::Command o;
o = new global::Command();
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
// code elided
//...
while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) {
if (Reader.NodeType == System.Xml.XmlNodeType.Element) {
if (((object)Reader.LocalName == (object)id4_To && (object)Reader.NamespaceURI == (object)id2_Item)) {
if (!ReadNull()) {
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
// code elided
//...
}
else {
// Problem here:
if ((object)(o.@To) == null) o.@To = new global::System.Collections.Generic.List<global::Parameter>();
global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)o.@To;
}
}
}
Reader.MoveToContent();
CheckReaderCount(ref whileIterations1, ref readerCount1);
}
ReadEndElement();
return o;
Не менее 3 мест, где проверяется, что свойство @To не равно нулю. Первый в некоторой степени оправдан: данные трудно десериализовать, когда структура не существует. Второй выполняет нулевой тест снова , это единственный действительно хороший тест. Третья проблема - ReadNull () вернула истину, но по-прежнему создает ненулевое значение свойства.
Если вы хотите различать пустые и пустые значения, у вас нет хорошего решения, кроме как отредактировать этот код вручную. Сделайте это только , если вы действительно в отчаянии и класс на 100% стабилен. Что ж, не делай этого. Решение Жуана - единственное хорошее.
Я согласен с комментарием @Oliver, но вы можете решить эту проблему следующим образом, если вам абсолютно необходимо, чтобы она возвращала null. Вместо использования автоматического свойства создайте свое собственное резервное поле.
List<Parameter> _to;
public List<Parameter> To
{
get
{
if (_to != null && _to.Count == 0) return null;
return _to;
}
set { _to = value; }
}
Если вам действительно нужно, чтобы коллекция десериализовалась в null
, когда значения не указаны, вы можете сделать это, не предоставив метод доступа set
, например:
public class Command
{
private List<Parameter> to;
public List<Parameter> To { get { return this.to; } }
}