Serializza un int nullable


92

Ho una classe con un int nullable? tipo di dati impostato per serializzare come elemento xml. C'è un modo per configurarlo in modo che il serializzatore xml non serializzi l'elemento se il valore è nullo?

Ho provato ad aggiungere l'attributo [System.Xml.Serialization.XmlElement (IsNullable = false)], ma ottengo un'eccezione di serializzazione di runtime che dice che si è verificato un errore che riflette il tipo, perché "IsNullable potrebbe non essere impostato su 'false "per un tipo nullable. Prendi in considerazione l'utilizzo del tipo" System.Int32 "o la rimozione della proprietà IsNullable dall'attributo 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; 
        } 
    }
     ...
}

La classe precedente verrà serializzata in:

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

Ma per gli ID che sono nulli non voglio affatto l'elemento ID, principalmente perché quando uso OPENXML in MSSQL, restituisce uno 0 invece di nullo per un elemento che assomiglia

Risposte:


149

XmlSerializer supporta il ShouldSerialize{Foo}()pattern, quindi puoi aggiungere un metodo:

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

C'è anche il {Foo}Specifiedpattern, non sono sicuro che XmlSerializer lo supporti.


8
XmlSerializer supporta anche il modello specificato [Foo}.
David Schmitt


1
Qualche modo per utilizzare ShouldSerialize <prop> con proprietà generate automaticamente? cioè nessuna variabile locale.
Jay

1
@ Jay: non ce n'è bisogno. Puoi semplicemente chiamare HasValuela proprietà.
Steven Sudit

1
@mark se, per il membro (proprietà / campo) Foohai anche un public bool FooSpecified {get {...} set {...}}, allora getviene usato per vedere se Foodeve essere serializzato e setviene chiamato quando si assegna un valore Foodurante la deserializzazione.
Marc Gravell

26

Sto usando questo micro-pattern per implementare la serializzazione 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; } }

Questo fornisce la giusta interfaccia all'utente senza compromessi e fa ancora la cosa giusta durante la serializzazione.


1
Poiché SomeValue può essere null ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } imposta {SomeValue = value; }}
Doug Domeny

XmlSomeValue dovrebbe essere usato solo da XmlSerializer che lo toccherà solo quando XmlSomeValueSpecified è vero (cioè SomeValue.Value non è nullo.
David Schmitt,

@pettys: è XML, cosa ti aspetti? ;-)
David Schmitt

La risposta accettata è del 2008. Questa dovrebbe essere quella attuale. Interessante risposta relativa a Specified vs ShouldSerialize
daniloquio

Sicuramente dovrebbe essere la risposta migliore.
tyteen4a03

12

Ho trovato una soluzione alternativa utilizzando due proprietà. Un int? con un attributo XmlIgnore e una proprietà oggetto che viene serializzata.

    /// <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());
            }
        }
    }

Questa soluzione è ottima in quanto permette anche di codificare NULL come valore "segnaposto" per i client, che non riconoscono NULL negli int, cioè Flex.
Kuba Wyrostek

Puoi aggiungere [EditorBrowsable (EditorBrowsableState.Never)] alla proprietà serializzata xml per averla vista durante la codifica
Antonio Rodríguez

6

Wow, grazie questa domanda / risposta mi ha davvero aiutato. Mi piace Stackoverflow.

Ho reso quello che stai facendo sopra un po 'più generico. Tutto ciò che stiamo veramente cercando è avere Nullable con un comportamento di serializzazione leggermente diverso. Ho usato Reflector per creare il mio Nullable e ho aggiunto alcune cose qua e là per far funzionare la serializzazione XML nel modo desiderato. Sembra funzionare abbastanza bene:

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

Perdi la possibilità di avere i tuoi membri come int? e così via (devi invece usare Nullable <int>) ma a parte questo tutto il comportamento rimane lo stesso.


1
Questo getta una System.ExecutionEngineExceptionsu XmlSerializer.Serializedi me.
Martin Braun

1

Sfortunatamente, i comportamenti descritti sono accuratamente documentati come tali nei documenti per XmlElementAttribute.IsNullable.


1

La pubblicazione di messaggi molto utili ha aiutato molto.

Ho scelto di andare con la revisione di Scott al tipo di dati Nullable (Of T), tuttavia il codice pubblicato serializza ancora l'elemento Nullable quando è Null, anche se senza l'attributo "xs: nil = 'true'".

Avevo bisogno di forzare il serializzatore a rilasciare completamente il tag, quindi ho semplicemente implementato IXmlSerializable sulla struttura (questo è in VB ma ottieni l'immagine):

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

Preferisco questo metodo all'utilizzo del modello specificato (pippo) in quanto richiede l'aggiunta di carichi di bucket di proprietà ridondanti ai miei oggetti, mentre l'utilizzo del nuovo tipo Nullable richiede solo la ridigitazione delle proprietà.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.