web-dev-qa-db-fra.com

ShouldSerialize * () vs * Modèle de sérialisation conditionnel spécifié

Je connais à la fois le modèle ShouldSerialize * et le modèle * Specified et comment ils fonctionnent, mais y a-t-il une différence entre les deux?

Existe-t-il des "accrochages" utilisant une méthode par rapport à l'autre lorsque certaines choses doivent être sérialisées conditionnellement?

Cette question est spécifique à l'utilisation de XmlSerializer, mais les informations générales concernant ce sujet sont également les bienvenues.

Il y a très peu d'informations sur ce sujet, donc c'est peut-être parce qu'elles remplissent exactement le même objectif et c'est un choix de style. Cependant, il semble étrange que les implémenteurs .NET analysent la classe par réflexion et recherchent l'un ou les deux modèles pour déterminer le comportement du sérialiseur généré, car il ralentit la génération du sérialiseur, sauf s'il s'agit simplement d'un artefact de compatibilité descendante.

EDIT: Pour ceux qui ne connaissent pas les deux modèles si soit le *Specified propriété ou ShouldSerialize* la méthode renvoie true, puis cette propriété est sérialisée.

public string MyProperty { get; set; }

//*Specified Pattern
[XmlIgnore]
public bool MyPropertySpecified { get{ return !string.IsNullOrWhiteSpace(this.MyProperty); } }

//ShouldSerialize* Pattern
public bool ShouldSerializeMyProperty()
{
     return !string.IsNullOrWhiteSpace(this.MyProperty);
}
22
JNYRanger

L'intention du modèle {propertyName}Specified Est documentée dans XML Schema Binding Support: MinOccurs Attribute Binding Support . Il a été ajouté pour prendre en charge un élément de schéma XSD dans lequel:

  • L'élément <element> est impliqué.
  • minOccurs vaut zéro.
  • L'attribut maxOccurs dicte une seule instance.
  • Le type de données est converti en type de valeur.

Dans ce cas, xsd.exe /classes générera automatiquement (ou vous pouvez générer manuellement) une propriété portant le même nom que l'élément de schéma et une propriété booléenne get/set {propertyName}Specified qui suit si l'élément a été rencontré dans le XML et doit être sérialisé en XML. Si l'élément est rencontré, {propertyName}Specified est réglé sur true, sinon false. Ainsi, l'instance désérialisée peut déterminer si la propriété n'a pas été définie (plutôt que définie explicitement à sa valeur par défaut) dans le XML d'origine.

L'inverse est également implémenté pour la génération de schéma. Si vous définissez un type C # avec une paire de propriétés correspondant au modèle ci-dessus, puis utilisez xsd.exe Pour générer un fichier XSD correspondant, un minOccurrs approprié sera ajouté au schéma. Par exemple, étant donné le type suivant:

public class ExampleClass
{
    [XmlElement]
    public decimal Something { get; set; }

    [XmlIgnore]
    public bool SomethingSpecified { get; set; }
}

Le schéma suivant sera généré et vice versa:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="ExampleClass" nillable="true" type="ExampleClass" />
  <xs:complexType name="ExampleClass">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Notez que, alors que xsd.exe Est documenté uniquement pour générer automatiquement une propriété {propertyName}Specified Pour les propriétés de type valeur, XmlSerializer respectera le modèle lorsqu'il est utilisé manuellement pour les propriétés de type référence.

Vous pourriez vous demander pourquoi xsd.exe Ne se lie pas à un Nullable<T> Dans ce cas? Peut-être parce que:

Vous devez être conscient de ce modèle car xsd.exe Le génère parfois automatiquement pour vous, mais l'interaction entre une propriété et sa propriété Specified est étrange et susceptible de produire des bogues. Vous pouvez remplir toutes les propriétés de votre classe, puis sérialiser en XML et perdre tout car vous n'avez pas non plus défini définir les propriétés Specified correspondantes sur true. Ce "gotcha" revient ici de temps en temps ici, voir par exemple cette question ou celle-ci aussi .

Un autre "problème" avec ce modèle est que, si vous avez besoin de sérialiser votre type avec un sérialiseur qui ne prend pas en charge ce modèle, vous pouvez vouloir manuellement supprimer la sortie de cette propriété pendant la sérialisation, et devra probablement la définir manuellement pendant la désérialisation. Étant donné que chaque sérialiseur peut avoir son propre mécanisme personnalisé pour supprimer les propriétés (ou aucun mécanisme du tout!), Cela peut devenir de plus en plus fastidieux au fil du temps.

(Enfin, je suis un peu surpris que votre MyPropertySpecified fonctionne correctement sans setter. Il me semble me rappeler une version de .Net 2.0 dans laquelle un setter {propertyName}Specified Manquant provoquerait une exception. Mais il n'est plus reproductible sur les versions ultérieures, et je n'ai pas 2.0 à tester. Cela pourrait donc être un troisième problème.)

La prise en charge de la méthode ShouldSerialize{PropertyName}() est documentée dans Propriétés dans les contrôles Windows Forms: définition des valeurs par défaut avec les méthodes ShouldSerialize et Reset . Comme vous pouvez le voir, la documentation se trouve dans la section Windows Forms de MSDN et non dans la section XmlSerializer, il s'agit donc en fait d'une fonctionnalité semi-masquée. Je ne sais pas pourquoi la prise en charge de cette méthode et la propriété Specified existent toutes les deux dans XmlSerializer. ShouldSerialize a été introduit dans . Net 1.1 et I croyez que le support de liaison MinOccurs a été ajouté dans . Net 2. , donc peut-être que la fonctionnalité antérieure ne répondait pas tout à fait aux besoins (ou aux goûts) de l'équipe de développement xsd.exe?

Parce qu'il s'agit d'une méthode et non d'une propriété, il lui manque les "accrochages" du modèle {propertyName}Specified. Il semble également être plus populaire dans la pratique et a été adopté par d'autres sérialiseurs, notamment:

Alors, quel modèle utiliser?

  1. Si xsd.exe Génère automatiquement une propriété {propertyName}Specified Pour vous, ou si votre type doit suivre si un élément spécifique est apparu ou non dans le fichier XML, ou si vous avez besoin de votre XSD généré automatiquement pour indiquer qu'un certaine valeur est facultative, utilisez ce modèle et faites attention aux "pièges".

  2. Sinon, utilisez le modèle ShouldSerialize{PropertyName}(). Il a moins de problèmes et peut être plus largement pris en charge.

35
dbc

Pour ajouter à la réponse très détaillée de @dbc, j'ai rencontré un problème avec la gestion de la sérialisation dans les classes dérivées. Dans ma situation, j'avais une classe de base et une classe dérivée où une propriété Prop était remplacée.

public class BaseClass
{
    public virtual string Prop {get; set;}
}

public class Derived: BaseClass
{
    public string Comp1 {get; set;}
    public string Comp2 {get; set;}
    public override string Prop {get => Comp1 + Comp2; set {}}
}

Étant donné que la propriété Prop dans la classe dérivée est calculée, pour la classe Derived, je voulais sérialiser Comp1 et Comp2 mais pas Prop. Il s'avère que la définition de l'attribut XmlIgnore sur la propriété Prop dans la classe Derived ne fonctionne pas et que Prop est de toute façon sérialisé.

J'ai également essayé d'ajouter une méthode ShouldSerializeProp et une propriété PropSpecified dans la classe Derived, mais aucun n'a fonctionné. J'ai essayé de définir des points d'arrêt pour voir s'ils sont appelés et ils ne le sont pas.

Il s'avère que XmlSerializer regarde la classe d'origine où la propriété Prop apparaît pour la première fois dans la hiérarchie des classes pour décider de sérialiser ou non une propriété. Pour pouvoir contrôler la sérialisation dans une classe dérivée, j'ai d'abord dû ajouter un virtual ShouldSerializeProp dans la classe Base.

public class Base
{
    .....
    public virtual bool ShouldSerializeProp() {return true;}
}

Ensuite, je pouvais remplacer la ShouldSerializeProp dans la classe Derived et retourner false.

public class Derived: Base
{
    .....
    public override bool ShouldSerializeProp() {return false;}
}

Ce modèle permet à différentes classes dérivées de choisir les propriétés de la classe parente qu'elles sérialisent. J'espère que cela t'aides.

6
Tibi