Serialisieren Sie einen nullbaren Int

92

Ich habe eine Klasse mit einem nullbaren int? Datentyp, der als XML-Element serialisiert werden soll. Gibt es eine Möglichkeit, es so einzurichten, dass der XML-Serializer das Element nicht serialisiert, wenn der Wert null ist?

Ich habe versucht, das Attribut [System.Xml.Serialization.XmlElement (IsNullable = false)] hinzuzufügen, erhalte jedoch eine Laufzeit-Serialisierungsausnahme, die besagt, dass ein Fehler aufgetreten ist, der den Typ widerspiegelt, da "IsNullable möglicherweise nicht auf" false "gesetzt ist 'für einen Nullable-Typ. Verwenden Sie den Typ' System.Int32 'oder entfernen Sie die IsNullable-Eigenschaft aus dem XmlElement-Attribut. "

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

Die obige Klasse wird serialisiert zu:

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

Aber für IDs, die null sind, möchte ich das ID-Element überhaupt nicht, vor allem, weil bei Verwendung von OPENXML in MSSQL für ein Element, das aussieht, eine 0 anstelle von null zurückgegeben wird

Jeremy
quelle

Antworten:

149

XmlSerializer unterstützt das ShouldSerialize{Foo}()Muster, sodass Sie eine Methode hinzufügen können:

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

Es gibt auch das {Foo}SpecifiedMuster - nicht sicher, ob XmlSerializer dieses unterstützt.

Marc Gravell
quelle
8
XmlSerializer unterstützt auch das von [Foo} angegebene Muster.
David Schmitt
1
Gibt es eine Möglichkeit, ShouldSerialize <prop> mit automatisch generierten Eigenschaften zu verwenden? dh keine lokale Variable.
Jay
1
@ Jay: Keine Notwendigkeit für eine. Sie können einfach HasValueauf dem Grundstück anrufen .
Steven Sudit
1
@mark Wenn Sie für member (Eigenschaft / Feld) Fooauch ein haben public bool FooSpecified {get {...} set {...}}, wird das getverwendet, um zu sehen, ob Fooes serialisiert werden soll, und das setwird aufgerufen, wenn Foowährend der Deserialisierung ein Wert zugewiesen wird.
Marc Gravell
26

Ich verwende dieses Mikromuster, um eine nullbare Serialisierung zu implementieren:

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

Dies bietet dem Benutzer die richtige Schnittstelle ohne Kompromisse und macht beim Serialisieren immer noch das Richtige.

David Schmitt
quelle
1
Da SomeValue null sein kann ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } set {SomeValue = value; }}
Doug Domeny
XmlSomeValue sollte nur vom XmlSerializer verwendet werden, der es nur berührt, wenn XmlSomeValueSpecified wahr ist (dh SomeValue.Value ist nicht null.
David Schmitt
@pettys: Es ist XML, was erwartest du? ;-)
David Schmitt
Die akzeptierte Antwort stammt aus dem Jahr 2008. Diese sollte jetzt diejenige sein. Interessante Antwort im Zusammenhang mit Specified vs ShouldSerialize
daniloquio
Auf jeden Fall sollte die beste Antwort sein.
tyteen4a03
12

Ich habe eine Problemumgehung mit zwei Eigenschaften gefunden. Ein int? Eigenschaft mit einem XmlIgnore-Attribut und einer Objekteigenschaft, die serialisiert wird.

    /// <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
quelle
Diese Lösung ist großartig, da NULL auch als "Platzhalter" -Wert für Clients codiert werden kann, die NULL-Werte in Ints, dh Flex, nicht erkennen.
Kuba Wyrostek
Sie können [EditorBrowsable (EditorBrowsableState.Never)] zur serialisierten xml-Eigenschaft hinzufügen, um sie beim Codieren zu sehen
Antonio Rodríguez
6

Wow, danke, diese Frage / Antwort hat mir wirklich geholfen. Ich liebe Stackoverflow.

Ich habe das, was Sie oben tun, etwas allgemeiner gemacht. Alles, was wir wirklich suchen, ist Nullable mit etwas anderem Serialisierungsverhalten. Ich habe Reflector verwendet, um meine eigene Nullable zu erstellen, und hier und da ein paar Dinge hinzugefügt, damit die XML-Serialisierung so funktioniert, wie wir es wollen. Scheint ziemlich gut zu funktionieren:

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

Sie verlieren die Fähigkeit, Ihre Mitglieder als int zu haben? und so weiter (müssen stattdessen Nullable <int> verwenden), aber ansonsten bleibt das gesamte Verhalten gleich.

scobi
quelle
1
Dies wirft ein System.ExecutionEngineExceptionauf XmlSerializer.Serializemich an .
Martin Braun
1

Leider sind die von Ihnen beschriebenen Verhaltensweisen in den Dokumenten für XmlElementAttribute.IsNullable als solche genau dokumentiert.

Serge Wautier
quelle
1

Sehr nützliches Posting hat sehr geholfen.

Ich habe mich für Scotts Überarbeitung des Datentyps Nullable (Of T) entschieden, aber der veröffentlichte Code serialisiert das Nullable-Element immer noch, wenn es Null ist - allerdings ohne das Attribut "xs: nil = 'true'".

Ich musste den Serializer zwingen, das Tag vollständig zu löschen, also habe ich einfach IXmlSerializable in die Struktur implementiert (dies ist in VB, aber Sie erhalten das Bild):

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

Ich bevorzuge diese Methode gegenüber der Verwendung des (foo) angegebenen Musters, da dies das Hinzufügen von Bucket-Lasten redundanter Eigenschaften zu meinen Objekten erfordert, während die Verwendung des neuen Nullable-Typs nur das erneute Eingeben der Eigenschaften erfordert.

James Close
quelle