Сериализовать значение nullable int

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

Я попытался добавить атрибут [System.Xml.Serialization.XmlElement(IsNullable = false)], но я получил исключение сериализации во время выполнения, указав, что произошла ошибка, отражающая тип, поскольку "IsNullable" может не быть установлен на "false" для типа Nullable. Рассмотрим использование типа "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>

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

Ответ 1

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

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

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

Ответ 2

Я использую этот микро-шаблон для реализации Nullable serialization:

[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; } }

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

Ответ 3

Я выяснил обходное решение, использующее два свойства. 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());
            }
        }
    }

Ответ 4

Ничего себе, этот вопрос/ответ действительно помог мне. я сердце 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> ), но кроме этого все поведение остается прежним.

Ответ 5

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

Ответ 6

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

Я решил перейти с версией Скотта к типу 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

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