39 votes

Force la sérialisation XML à sérialiser la propriété readonly

En C#, j'ai une classe qui possède une propriété dérivée qui doit être sérialisée via XML. Cependant, la sérialisation XML (par défaut) ne sérialise pas les propriétés en lecture seule. Je peux contourner ce problème en définissant un setter vide comme suit :

public virtual string IdString
{
    get { return Id.ToString("000000"); }
    set { /* required for xml serialization */ }
}

Mais existe-t-il un moyen plus propre et plus correct d'un point de vue sémantique, sans écrire ma propre implémentation de ISerializable ?

20voto

Justin Points 42106

Honnêtement, cela ne me semble pas trop grave, tant que c'est documenté.

Vous devriez probablement lever une exception si le setter est effectivement appelé :

/// <summary>
/// Blah blah blah.
/// </summary>
/// <exception cref="NotSupportedException">Any use of the setter for this property.</exception>
/// <remarks>
/// This property is read only and should not be set.  
/// The setter is provided for XML serialisation.
/// </remarks>
public virtual string IdString
{
    get
    {
        return Id.ToString("000000");
    }
    set
    {
        throw new NotSupportedException("Setting the IdString property is not supported");
    }
}

14voto

Marc Gravell Points 482669

En bref, non. Avec XmlSerializer vous pouvez soit mettre en œuvre IXmlSerializable (ce qui n'est pas trivial), ou écrire un DTO de base (qui est entièrement en lecture-écriture) et ensuite traduire du modèle DTO à votre modèle principal.

Notez que dans certains cas DataContractSerializer est une option viable, mais elle n'offre pas le même contrôle sur le XML. Cependant, avec le DCS, vous pouvez le faire :

[DataMember]
public int Id { get; private set; }

2voto

Yair Halberstadt Points 1118

Avec C# 8, l'obsolescence set est autorisé, vous pouvez donc le faire :

public virtual string IdString
{
    get { return Id.ToString("000000"); }
    [Obsolete("Only used for xml serialization", error: true)]
    set { throw new NotSupportedException(); }
}

Cela provoquera une erreur si quelqu'un utilise le setter accidentellement.

1voto

Pour pousser la solution un peu plus loin et permettre à la désérialisation de fonctionner également...

public class A
{
    private int _id = -1;

    public int Id
    {
        get { return _id; }
        set
        {
            if (_id < 0)
                throw new InvalidOperationException("...");

            if (value < 0)
                throw new ArgumentException("...");

            _id = value;
        }
    }
}

Cela permettra Id ne doit être défini qu'une seule fois à une valeur supérieure ou égale à 0. Toute tentative de le définir par la suite se soldera par un échec. InvalidOperationException . Cela signifie que XmlSerializer sera en mesure de définir Id pendant la désérialisation, mais il ne pourra jamais être modifié après. Notez que si la propriété est un type de référence, vous pouvez simplement vérifier si elle est nulle.

Ce n'est peut-être pas la meilleure solution si vous avez beaucoup de propriétés en lecture seule à sérialiser/désérialiser, car cela nécessiterait beaucoup de code passe-partout. Cependant, j'ai trouvé cette solution acceptable pour les classes avec 1 à 2 propriétés en lecture seule.

C'est toujours un hack, mais c'est au moins un petit plus robuste.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X