Sérialiser un int nullable


92

J'ai une classe avec un int nullable? type de données défini pour être sérialisé en tant qu'élément xml. Existe-t-il un moyen de le configurer pour que le sérialiseur XML ne sérialisera pas l'élément si la valeur est nulle?

J'ai essayé d'ajouter l'attribut [System.Xml.Serialization.XmlElement (IsNullable = false)], mais j'obtiens une exception de sérialisation d'exécution indiquant qu'il y avait une erreur reflétant le type, car "IsNullable peut ne pas être défini sur 'false 'pour un type Nullable. Pensez à utiliser le type' System.Int32 'ou à supprimer la propriété IsNullable de l'attribut 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 ci-dessus sera sérialisée pour:

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

Mais pour les ID qui sont nuls, je ne veux pas du tout l'élément ID, principalement parce que lorsque j'utilise OPENXML dans MSSQL, il renvoie un 0 au lieu de null pour un élément qui ressemble à

Réponses:


149

XmlSerializer prend en charge le ShouldSerialize{Foo}()modèle, vous pouvez donc ajouter une méthode:

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

Il y a aussi le {Foo}Specifiedmodèle - je ne sais pas si XmlSerializer prend en charge celui-là.


8
XmlSerializer prend également en charge le modèle spécifié [Foo}.
David Schmitt


1
Un moyen d'utiliser ShouldSerialize <prop> avec des propriétés générées automatiquement? c'est-à-dire pas de variable locale.
Jay

1
@Jay: Pas besoin d'en avoir un. Vous pouvez simplement appeler HasValuela propriété.
Steven Sudit

1
@mark si, pour le membre (propriété / champ), Foovous avez également un public bool FooSpecified {get {...} set {...}}, alors le getest utilisé pour voir s'il Foodoit être sérialisé, et le setest appelé lors de l'affectation d'une valeur à Foopendant la désérialisation.
Marc Gravell

26

J'utilise ce micro-modèle pour implémenter la sérialisation 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; } }

Cela fournit la bonne interface à l'utilisateur sans compromis et fait toujours la bonne chose lors de la sérialisation.


1
Puisque SomeValue peut être nul ... public double XmlSomeValue {get {return SomeValue.HasValue? SomeValue.Value: 0; } set {SomeValue = valeur; }}
Doug Domeny

XmlSomeValue n'est censé être utilisé que par le XmlSerializer qui ne le touchera que lorsque XmlSomeValueSpecified est vrai (c'est-à-dire SomeValue.Value n'est pas nul.
David Schmitt

@pettys: C'est du XML, qu'attendez-vous? ;-)
David Schmitt

La réponse acceptée date de 2008. Celle-ci devrait être celle du moment. Réponse intéressante liée à Specified vs ShouldSerialize
daniloquio

Cela devrait certainement être la meilleure réponse.
tyteen4a03

12

J'ai trouvé une solution de contournement en utilisant deux propriétés. Un int? propriété avec un attribut XmlIgnore et une propriété d'objet qui est sérialisée.

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

Cette solution est excellente car elle permet également d'encoder NULL en tant que valeur "placeholder" pour les clients, qui ne reconnaissent pas les NULL dans les ints, c'est-à-dire Flex.
Kuba Wyrostek

Vous pouvez ajouter [EditorBrowsable (EditorBrowsableState.Never)] à la propriété sérialisée xml pour éviter de la voir lors du codage
Antonio Rodríguez

6

Wow merci cette question / réponse m'a vraiment aidé. Je cœur Stackoverflow.

J'ai rendu ce que vous faites ci-dessus un peu plus générique. Tout ce que nous recherchons vraiment, c'est d'avoir Nullable avec un comportement de sérialisation légèrement différent. J'ai utilisé Reflector pour créer mon propre Nullable, et j'ai ajouté quelques éléments ici et là pour que la sérialisation XML fonctionne comme nous le souhaitons. Semble fonctionner plutôt bien:

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

Vous perdez la possibilité d'avoir vos membres comme int? et ainsi de suite (il faut utiliser Nullable <int> à la place) mais à part cela, tout le comportement reste le même.


1
Cela me jette un coup System.ExecutionEngineExceptiond' XmlSerializer.Serializeœil.
Martin Braun

1

Malheureusement, les comportements que vous décrivez sont précisément documentés en tant que tels dans la documentation de XmlElementAttribute.IsNullable.


1

Une publication très utile a beaucoup aidé.

J'ai choisi d'aller avec la révision de Scott pour le type de données Nullable (Of T), mais le code affiché sérialise toujours l'élément Nullable quand il est Null - bien que sans l'attribut "xs: nil = 'true'".

J'avais besoin de forcer le sérialiseur à supprimer complètement la balise, alors j'ai simplement implémenté IXmlSerializable sur la structure (c'est en VB mais vous obtenez l'image):

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

Je préfère cette méthode à l'utilisation du modèle spécifié (foo) car cela nécessite l'ajout de charges de seau de propriétés redondantes à mes objets, alors que l'utilisation du nouveau type Nullable nécessite simplement la retaper des propriétés.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.