web-dev-qa-db-fra.com

Sérialiser un entier nullable

J'ai une classe avec un entier nullable? type de données défini pour sérialiser en tant qu'élément xml. Existe-t-il un moyen de le configurer pour que le sérialiseur xml ne sérialise 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 ne peut 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 à

87
Jeremy

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

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

Il y a aussi le {Foo}Specified pattern - je ne sais pas si XmlSerializer prend en charge celui-ci.

147
Marc Gravell

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.

25
David Schmitt

J'ai trouvé une solution de contournement utilisant deux propriétés. Un int? 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());
            }
        }
    }
12
Jeremy

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, 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 assez bien fonctionner:

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 en tant qu'int? et ainsi de suite (doivent utiliser Nullable <int> à la place) mais à part ça, tout le comportement reste le même.

6
scobi

Malheureusement, les comportements que vous décrivez sont correctement documentés en tant que tels dans les documents pour XmlElementAttribute.IsNullable.

1
Serge Wautier

Une publication très utile a beaucoup aidé.

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

J'ai dû forcer le sérialiseur à supprimer complètement la balise, j'ai donc 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 (foo) spécifié car cela nécessite l'ajout de charges de compartiment de propriétés redondantes à mes objets, tandis que l'utilisation du nouveau type Nullable nécessite simplement la retapée des propriétés.

1
James Close