Serializuj int nullable

92

Mam klasę z wartością nullable int? typ danych ustawiony na serializację jako element xml. Czy istnieje sposób, aby skonfigurować to tak, aby serializator xml nie serializował elementu, jeśli wartość jest równa null?

Próbowałem dodać atrybut [System.Xml.Serialization.XmlElement (IsNullable = false)], ale pojawia się wyjątek serializacji środowiska wykonawczego z informacją, że wystąpił błąd odzwierciedlający typ, ponieważ „IsNullable może nie być ustawiona na„ false ” 'dla typu dopuszczającego wartość null. Rozważ użycie typu „System.Int32” lub usunięcie właściwości IsNullable z atrybutu 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; 
        } 
    }
     ...
}

Powyższa klasa będzie serializowana do:

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

Ale w przypadku identyfikatorów, które są zerowe, w ogóle nie chcę elementu ID, głównie dlatego, że kiedy używam OPENXML w MSSQL, zwraca 0 zamiast null dla elementu, który wygląda jak

Jeremy
źródło

Odpowiedzi:

149

XmlSerializer obsługuje ShouldSerialize{Foo}()wzorzec, więc możesz dodać metodę:

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

Jest też {Foo}Specifiedwzorzec - nie jestem pewien, czy XmlSerializer to obsługuje.

Marc Gravell
źródło
8
XmlSerializer obsługuje również wzorzec [Foo} Specified.
David Schmitt
23
Odpowiednia strona jest tutaj: msdn.microsoft.com/en-us/library/53b8022e%28VS.71%29.aspx
cbp
1
Jakikolwiek sposób na użycie ShouldSerialize <prop> z właściwościami generowanymi automatycznie? tj. brak zmiennej lokalnej.
Jay
1
@Jay: Nie ma takiej potrzeby. Możesz po prostu zadzwonić HasValuedo nieruchomości.
Steven Sudit
1
@mark, jeśli dla elementu członkowskiego (właściwość / pole) Foomasz również element public bool FooSpecified {get {...} set {...}}, getsłuży do sprawdzenia, czy Foopowinien być serializowany, a setjest wywoływany podczas przypisywania wartości do Foopodczas deserializacji.
Marc Gravell
26

Używam tego mikro-wzorca do implementacji serializacji 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; } }

Zapewnia to użytkownikowi właściwy interfejs bez kompromisów i nadal działa właściwie podczas serializacji.

David Schmitt
źródło
1
Ponieważ SomeValue może być null ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } ustaw {SomeValue = wartość; }}
Doug Domeny
XmlSomeValue ma być używane tylko przez XmlSerializer, który dotknie go tylko wtedy, gdy XmlSomeValueSpecified ma wartość true (tj. SomeValue.Value nie jest null.
David Schmitt
@pettys: To XML, czego oczekujesz? ;-)
David Schmitt
Przyjęta odpowiedź pochodzi z 2008 roku. Ta powinna być teraz. Ciekawa odpowiedź związana z Specified vs ShouldSerialize
daniloquio
Zdecydowanie powinna być najlepszą odpowiedzią.
tyteen4a03
12

Znalazłem obejście z wykorzystaniem dwóch właściwości. Int? właściwość z atrybutem XmlIgnore i właściwością obiektu, która jest serializowana.

    /// <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());
            }
        }
    }
Jeremy
źródło
To rozwiązanie jest świetne, ponieważ pozwala również na kodowanie NULL jako wartości „zastępczej” dla klientów, którzy nie rozpoznają wartości NULL w intach, czyli Flex.
Kuba Wyrostek
Możesz dodać [EditorBrowsable (EditorBrowsableState.Never)] do serializowanej właściwości xml, aby uniknąć zobaczenia go podczas kodowania
Antonio Rodríguez
6

Wow, dzięki, to pytanie / odpowiedź naprawdę mi pomogło. Serce Stackoverflow.

Sprawiłem, że to, co robisz, było trochę bardziej ogólne. Wszystko, czego naprawdę szukamy, to mieć wartość Nullable z nieco innym zachowaniem serializacji. Użyłem Reflectora do zbudowania własnej wartości Nullable i dodałem kilka rzeczy tu i tam, aby serializacja XML działała tak, jak chcemy. Wydaje się, że działa całkiem nieźle:

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

Tracisz możliwość posiadania członków jako int? i tak dalej (zamiast tego należy użyć Nullable <int>), ale poza tym wszystkie zachowania pozostają takie same.

scobi
źródło
1
To rzuca System.ExecutionEngineExceptionON XmlSerializer.Serializena mnie.
Martin Braun
1

Niestety opisane zachowania są dokładnie udokumentowane jako takie w dokumentacji XmlElementAttribute.IsNullable.

Serge Wautier
źródło
1

Bardzo przydatne posty bardzo pomogły.

Zdecydowałem się przejść z wersją Scotta do typu danych Nullable (Of T), jednak wysłany kod nadal serializuje element Nullable, gdy ma wartość Null - aczkolwiek bez atrybutu „xs: nil = 'true'”.

Musiałem wymusić na serializatorze całkowite usunięcie tagu, więc po prostu zaimplementowałem IXmlSerializable na strukturze (to jest w VB, ale masz zdjęcie):

  '----------------------------------------------------------------------------
  ' 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

Wolę tę metodę niż użycie wzorca (foo) Specified, ponieważ wymaga to dodania ładunków zasobnika nadmiarowych właściwości do moich obiektów, podczas gdy użycie nowego typu Nullable wymaga tylko ponownego wpisania właściwości.

James Close
źródło