Сериализовать обнуляемое int

92

У меня есть класс с обнуляемым int? тип данных установлен для сериализации как элемент xml. Есть ли способ настроить его так, чтобы сериализатор xml не сериализовал элемент, если значение равно нулю?

Я пытался добавить атрибут [System.Xml.Serialization.XmlElement (IsNullable = false)], но получаю исключение сериализации во время выполнения, в котором говорится, что произошла ошибка, отражающая тип, потому что для параметра IsNullable не может быть установлено значение 'false "для типа, допускающего значение NULL. Рассмотрите возможность использования типа" System.Int32 "или удаления свойства IsNullable из атрибута XmlElement".

[Serializable]
[System.Xml.Serialization.XmlRoot("Score", Namespace = "http://mycomp.com/test/score/v1")]
public class Score
{
    private int? iID_m;
    ...

    /// <summary>
    /// 
    /// </summary>        
    public int? ID 
    { 
        get 
        { 
            return iID_m; 
        } 
        set 
        { 
            iID_m = value; 
        } 
    }
     ...
}

Вышеупомянутый класс будет сериализован в:

<Score xmlns="http://mycomp.com/test/score/v1">
    <ID xsi:nil="true" />
</Score>

Но для идентификаторов с нулевым значением мне вообще не нужен элемент идентификатора, в первую очередь потому, что, когда я использую OPENXML в MSSQL, он возвращает 0 вместо нуля для элемента, который выглядит как

Джереми
источник

Ответы:

150

XmlSerializer поддерживает ShouldSerialize{Foo}()шаблон, поэтому вы можете добавить метод:

public bool ShouldSerializeID() {return ID.HasValue;}

Есть также {Foo}Specifiedшаблон - не уверен, поддерживает ли его XmlSerializer.

Марк Гравелл
источник
8
XmlSerializer также поддерживает указанный шаблон [Foo}.
Дэвид Шмитт
23
Страница Relevent находится здесь: msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx
cbp
1
Любой способ использовать ShouldSerialize <prop> с автоматически сгенерированными свойствами? т.е. нет локальной переменной.
Джей
1
@Jay: В этом нет необходимости. Вы можете просто позвонить HasValueв собственность.
Стивен Судит,
1
@mark, если для члена (свойства / поля) у Fooвас также есть a public bool FooSpecified {get {...} set {...}}, то getиспользуется, чтобы увидеть, Fooследует ли сериализовать, и setвызывается при присвоении значения во Fooвремя десериализации.
Марк Грейвелл
26

Я использую этот микро-шаблон для реализации сериализации, допускающей значение NULL:

[XmlIgnore]
public double? SomeValue { get; set; }

[XmlAttribute("SomeValue")] // or [XmlElement("SomeValue")]
[EditorBrowsable(EditorBrowsableState.Never)]
public double XmlSomeValue { get { return SomeValue.Value; } set { SomeValue= value; } }  
[EditorBrowsable(EditorBrowsableState.Never)]
public bool XmlSomeValueSpecified { get { return SomeValue.HasValue; } }

Это обеспечивает правильный интерфейс для пользователя без компромиссов и по-прежнему делает правильные вещи при сериализации.

Дэвид Шмитт
источник
1
Поскольку SomeValue может иметь значение null ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } установить {SomeValue = значение; }}
Дуг Домени 05
Предполагается, что XmlSomeValue будет использоваться только XmlSerializer, который будет касаться его только тогда, когда XmlSomeValueSpecified имеет значение true (т.е. SomeValue.Value не равно null.
Дэвид Шмитт
@pettys: Это XML, чего вы ждете? ;-)
Дэвид Шмитт
Принятый ответ от 2008 года. Этот должен быть сейчас. Интересный ответ, связанный с Specified vs ShouldSerialize
daniloquio
Определенно должен быть главный ответ.
tyteen4a03 04
12

Я нашел обходной путь, используя два свойства. Int? свойство с атрибутом XmlIgnore и свойством объекта, которое сериализуется.

    /// <summary>
    /// Score db record
    /// </summary>        
    [System.Xml.Serialization.XmlIgnore()]
    public int? ID 
    { 
        get 
        { 
            return iID_m; 
        } 
        set 
        { 
            iID_m = value; 
        } 
    }

    /// <summary>
    /// Score db record
    /// </summary>        
    [System.Xml.Serialization.XmlElement("ID",IsNullable = false)]
    public object IDValue
    {
        get
        {
            return ID;
        }
        set
        {
            if (value == null)
            {
                ID = null;
            }
            else if (value is int || value is int?)
            {
                ID = (int)value;
            }
            else
            {
                ID = int.Parse(value.ToString());
            }
        }
    }
Джереми
источник
Это отличное решение, так как оно также позволяет кодировать NULL в качестве значения «заполнителя» для клиентов, которые не распознают NULL в целочисленных значениях, т.е. Flex.
Kuba Wyrostek
Вы можете добавить [EditorBrowsable (EditorBrowsableState.Never)] в сериализованное свойство xml, чтобы он не видел его при кодировании
Антонио Родригес
6

Вау, спасибо, этот вопрос / ответ мне действительно помог. Я люблю Stackoverflow.

Я сделал то, что вы делаете выше, более общим. Все, что нам действительно нужно, это иметь Nullable с немного другим поведением сериализации. Я использовал Reflector, чтобы создать свой собственный Nullable, и добавил кое-что здесь и там, чтобы сериализация XML работала так, как мы хотим. Кажется, неплохо работает:

public class Nullable<T>
{
    public Nullable(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Nullable()
    {
        _hasValue = false;
    }

    [XmlText]
    public T Value
    {
        get
        {
            if (!HasValue)
                throw new InvalidOperationException();
            return _value;
        }
        set
        {
            _value = value;
            _hasValue = true;
        }
    }

    [XmlIgnore]
    public bool HasValue
        { get { return _hasValue; } }

    public T GetValueOrDefault()
        { return _value; }
    public T GetValueOrDefault(T i_defaultValue)
        { return HasValue ? _value : i_defaultValue; }

    public static explicit operator T(Nullable<T> i_value)
        { return i_value.Value; }
    public static implicit operator Nullable<T>(T i_value)
        { return new Nullable<T>(i_value); }

    public override bool Equals(object i_other)
    {
        if (!HasValue)
            return (i_other == null);
        if (i_other == null)
            return false;
        return _value.Equals(i_other);
    }

    public override int GetHashCode()
    {
        if (!HasValue)
            return 0;
        return _value.GetHashCode();
    }

    public override string ToString()
    {
        if (!HasValue)
            return "";
        return _value.ToString();
    }

    bool _hasValue;
    T    _value;
}

Вы теряете возможность иметь своих членов как int? и так далее (вместо этого нужно использовать Nullable <int>), но в остальном все поведение остается прежним.

Скоби
источник
1
Это бросает System.ExecutionEngineExceptionON XmlSerializer.Serializeна меня.
Мартин Браун
1

К сожалению, описываемое вами поведение точно задокументировано как таковое в документации для XmlElementAttribute.IsNullable.

Серж Вотье
источник
1

Очень полезная публикация очень помогла.

Я решил пойти с ревизией Скотта для типа данных Nullable (Of T), однако опубликованный код по-прежнему сериализует элемент Nullable, когда он имеет значение Null, хотя и без атрибута «xs: nil = 'true'».

Мне нужно было заставить сериализатор полностью удалить тег, поэтому я просто реализовал IXmlSerializable в структуре (это в VB, но вы понимаете):

  '----------------------------------------------------------------------------
  ' GetSchema
  '----------------------------------------------------------------------------
  Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
    Return Nothing
  End Function

  '----------------------------------------------------------------------------
  ' ReadXml
  '----------------------------------------------------------------------------
  Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml
    If (Not reader.IsEmptyElement) Then
      If (reader.Read AndAlso reader.NodeType = System.Xml.XmlNodeType.Text) Then
         Me._value = reader.ReadContentAs(GetType(T), Nothing)
      End If
    End If
  End Sub

  '----------------------------------------------------------------------------
  ' WriteXml
  '----------------------------------------------------------------------------
  Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml
    If (_hasValue) Then
      writer.WriteValue(Me.Value)
    End If
  End Sub

Я предпочитаю этот метод использованию шаблона Specified (foo), так как для этого требуется добавить множество избыточных свойств к моим объектам, тогда как использование нового типа Nullable требует лишь повторного ввода свойств.

Джеймс Клоуз
источник