Серіалізувати нульовий int


92

У мене є клас із нульовим int? тип даних встановлений для серіалізації як елемент xml. Чи є спосіб налаштувати його, щоб серіалізатор xml не серіалізував елемент, якщо значення дорівнює нулю?

Я спробував додати атрибут [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 замість null для елемента, який виглядає як

Відповіді:


149

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

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

Існує також {Foo}Specifiedшаблон - не впевнений, що XmlSerializer підтримує цей.


8
XmlSerializer також підтримує шаблон [Foo}].
Девід Шмітт,

23
Відповідна сторінка тут: msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx
cbp

1
Будь-який спосіб використання ShouldSerialize <prop> з автоматично згенерованими властивостями? тобто немає локальної змінної.
Джей,

1
@Jay: Не потрібно жодного. Ви можете просто зателефонувати HasValueдо помешкання.
Steven Sudit

1
@mark, якщо для члена (властивість / поле) у Fooвас також є а public bool FooSpecified {get {...} set {...}}, тоді getвикористовується, щоб побачити, чи Fooслід серіалізуватись, і setвикликається при присвоєнні значення Fooпід час десеріалізації.
Марк Гравелл

26

Я використовую цей мікро-шаблон для реалізації Nullable серіалізації:

[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 може бути нульовим ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } встановити {SomeValue = значення; }}
Дуг Домені

XmlSomeValue повинен використовуватись лише XmlSerializer, який торкнеться його лише тоді, коли XmlSomeValueSpecified є істинним (тобто SomeValue.Value не є нульовим.
Девід Шмітт

@pettys: Це XML, що ти очікуєш? ;-)
Девід Шмітт

Прийнята відповідь 2008 року. Ця повинна бути такою зараз. Цікава відповідь, пов'язана з " Уточненим" чи "Потрібно серіалізувати"
daniloquio

Безумовно, це має бути головна відповідь.
tyteen4a03

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 у ints, тобто Flex.
Kuba Wyrostek

Ви можете додати [EditorBrowsable (EditorBrowsableState.Never)] до серіалізованого властивості xml, щоб aviod бачив це під час кодування
Антоніо Родрігес,

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.ExecutionEngineExceptionна 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

Я віддаю перевагу цьому методу використанню (foo) Зазначеного шаблону, оскільки для цього потрібно додати сегментні навантаження надлишкових властивостей до моїх об'єктів, тоді як використання нового типу Nullable просто вимагає перетипу властивостей.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.