Как сделать тип значения обнуляемым с .NET XmlSerializer? - PullRequest
28 голосов
/ 01 апреля 2009

Предположим, у меня есть этот объект:

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}

XmlSerializer будет сериализовать объект следующим образом:

<MyClass>
    <Age>0</age>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

Как я могу сделать свойство Age nullable? IE: чтобы не сериализовать свойство Age, когда оно меньше 0?

Я пытался с Nullable, но он сериализовал мой объект так:

<MyClass>
    <Age d5p1:nil="true" />
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>    

Читая документацию MSDN, я нашел это:

Нельзя применить свойство IsNullable к члену, типизированному как тип значения, поскольку тип значения не может содержать нулевую ссылку nullNothingnullptra (Nothing в Visual Basic). Кроме того, вы не можете установить для этого свойства значение false для типов значений Nullable. Когда такие типы имеют нулевую ссылку nullNothingnullptra (Nothing в Visual Basic), они будут сериализованы путем установки xsi: nil в значение true.

источник: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx

Я понимаю, что тип значения не может быть установлен равным нулю. Значение типа всегда установлено на что-то. Сериализация не может принять решение о сериализации или нет, основываясь на ее текущем значении.

Я попробовал с атрибутами, но это не сработало. Я пытался создать объект agecontainer и управлять его сериализацией с помощью атрибутов, но это не сработало.

Что я действительно хочу, это:

<MyClass>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

Когда свойство Age ниже 0 (ноль).


Похоже, вам придется реализовать пользовательскую сериализацию.

Да, я тоже об этом, но я бы хотел обойтись без этого.

В приложении объект намного сложнее, и я бы не хотел сам обрабатывать сериализацию.

Ответы [ 6 ]

53 голосов
/ 01 апреля 2009

Я только что обнаружил это. XmlSerialier ищет логическое свойство XXXSpecified, чтобы определить, должно ли оно быть включено. Это должно хорошо решить проблему.

[Serializable]
public class MyClass
{
  public int Age { get; set; }
  [XmlIgnore]
  public bool AgeSpecified { get { return Age >= 0; } }
  public int MyClassB { get; set; }
}

[Serializable]
public class MyClassB
{
  public int RandomNumber { get; set; }
}

Доказательство:

static string Serialize<T>(T obj)
{
  var serializer = new XmlSerializer(typeof(T));
  var builder = new StringBuilder();
  using (var writer = new StringWriter(builder))
  {
    serializer.Serialize(writer, obj);
    return builder.ToString();
  }
}

static void Main(string[] args)
{
  var withoutAge = new MyClass() { Age = -1 };
  var withAge = new MyClass() { Age = 20 };

  Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
  Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
}
<ч />

Редактировать : Да, это документированная функция. См. MSDN запись для XmlSerializer

Другой вариант - использовать специальный шаблон для создания логического поля, распознаваемого XmlSerializer, и применять XmlIgnoreAttribute к полю. Шаблон создается в виде propertyNameSpecified. Например, если есть поле с именем «MyFirstName», вы также должны создать поле с именем «MyFirstNameSpecified», которое указывает XmlSerializer, генерировать ли элемент XML с именем «MyFirstName».

13 голосов
/ 11 декабря 2009

Расширение ответа Самуэля и комментария Грега Бича на случай логического свойства: если свойство имеет тип bool, то вы не можете написать простой тест в свойстве propertySpecified.

Решением является использование типа Nullable , тогда тест в свойстве propertySpecified - это просто property.HasValue. например,

using System.Xml.Serialization;

public class Person
{
    public bool? Employed { get; set; }

    [XmlIgnore]
    public bool EmployedSpecified { get { return Employed.HasValue; } }
}

Альтернативой использованию обнуляемого типа для числового свойства (предложенного Грегом Бичем) является присвоение свойству значения недопустимого значения по умолчанию, например -1, следующим образом:

using System.ComponentModel;
using System.Xml.Serialization;

public class Person
{
    [DefaultValue(-1)]
    public int Age { get; set; }

    [XmlIgnore]
    public bool AgeSpecified { get { return Age >= 0; } }
}
4 голосов
/ 18 декабря 2013

Вы можете использовать XmlElementAttribute.IsNullable :

[Serializable]
public class MyClass
{
    [XmlElement(IsNullable = true)]
    public int? Age { get; set; }

    public int MyClassB { get; set; }
}
2 голосов
/ 01 апреля 2009

Это должно помочь Сделать возраст int? и ..

public bool ShouldSerializeAge() { return Age.HasValue; }

.. это означает добавление методов ShouldSerializeXXX в ваш класс!

0 голосов
/ 16 марта 2010

xsd.exe автоматически сгенерирует свойство и методы доступа XXXSpecified, если вы установите для атрибута 'minoccurs' значение 'minoccurs = "0"' для элемента ... если вы используете схему для определения вашего xml / class

0 голосов
/ 12 ноября 2009

Забудьте о Nullable ... ShouldSerializeXXX - симпатичное решение. Здесь Age будет сериализовано в зависимости от вашего состояния.

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }

    #region Conditional Serialization
    public bool ShouldSerializeAge() { return age > 0; }
    #endregion
}

[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}
...