Как десериализовать пустой массив к пустому указателю в c#?

Вот мой класс:

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?

11
задан Groo 30 March 2010 в 09:57
поделиться

3 ответа

Ух, раздражает, правда? Вы можете увидеть это, запустив 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% стабилен. Что ж, не делай этого. Решение Жуана - единственное хорошее.

2
ответ дан 3 December 2019 в 11:20
поделиться

Я согласен с комментарием @Oliver, но вы можете решить эту проблему следующим образом, если вам абсолютно необходимо, чтобы она возвращала null. Вместо использования автоматического свойства создайте свое собственное резервное поле.

List<Parameter> _to;
public List<Parameter> To
{
    get
    {
        if (_to != null && _to.Count == 0) return null;
        return _to;
    }
    set { _to = value; }
}
0
ответ дан 3 December 2019 в 11:20
поделиться

Если вам действительно нужно, чтобы коллекция десериализовалась в null , когда значения не указаны, вы можете сделать это, не предоставив метод доступа set , например:

public class Command
{
    private List<Parameter> to;

    public List<Parameter> To { get { return this.to; } }
}
0
ответ дан 3 December 2019 в 11:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: