web-dev-qa-db-fra.com

Comment rendre un type de valeur nullable avec .NET XmlSerializer?

Supposons que j'ai cet objet:

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }
}
[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}

XmlSerializer va sérialiser l'objet comme ça:

<MyClass>
    <Age>0</age>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

Comment puis-je rendre la propriété Age nullable? IE: ne pas sérialiser la propriété Age quand il est sous 0?

J'ai essayé avec Nullable, mais il sérialise mon objet comme ça:

<MyClass>
    <Age d5p1:nil="true" />
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>    

En lisant la documentation MSDN, j'ai trouvé ceci:

Vous ne pouvez pas appliquer la propriété IsNullable à un membre tapé en tant que type de valeur car un type de valeur ne peut pas contenir nullNothingnullptra référence null (Nothing en Visual Basic). En outre, vous ne pouvez pas définir cette propriété sur false pour les types de valeur nullable. Lorsque ces types sont null référence nullNothingnullptra (rien dans Visual Basic), ils sont sérialisés en définissant xsi: nil sur true.

source: http://msdn.Microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable.aspx

Je comprends qu'un type de valeur ne peut pas être défini sur null. Un type de valeur est toujours défini sur quelque chose. La sérialisation ne peut pas décider de la sérialiser ou non en fonction de sa valeur actuelle.

J'ai essayé avec les attributs, mais ça n'a pas marché. J'ai essayé de créer un objet agecontainer et de manipuler sa sérialisation avec des attributs, mais cela n'a pas fonctionné.

Ce que je veux vraiment, c'est:

<MyClass>
    <MyClassB>
        <RandomNumber>4234</RandomNumber>
    </MyClassB>
</MyClass>

Lorsque la propriété Age est inférieure à 0 (zéro).


On dirait que vous devrez implémenter une sérialisation personnalisée.

Oui, c'est ce que je pensais aussi, mais j'aimerais bien m'en passer.

Dans l'application, l'objet est beaucoup plus complexe et je ne voudrais pas gérer la sérialisation moi-même.

28
Jean-Francois

Je viens de découvrir cela. XmlSerialier recherche une propriété XXXSpecified boolean pour déterminer si elle doit être incluse. Cela devrait résoudre le problème bien.

[Serializable]
public class MyClass
{
  public int Age { get; set; }
  [XmlIgnore]
  public bool AgeSpecified { get { return Age >= 0; } }
  public int MyClassB { get; set; }
}

[Serializable]
public class MyClassB
{
  public int RandomNumber { get; set; }
}

Preuve:

static string Serialize<T>(T obj)
{
  var serializer = new XmlSerializer(typeof(T));
  var builder = new StringBuilder();
  using (var writer = new StringWriter(builder))
  {
    serializer.Serialize(writer, obj);
    return builder.ToString();
  }
}

static void Main(string[] args)
{
  var withoutAge = new MyClass() { Age = -1 };
  var withAge = new MyClass() { Age = 20 };

  Serialize(withoutAge); // = <MyClass><MyClassB>0</MyClassB></MyClass>
  Serialize(withAge); // = <MyClass><Age>20</Age><MyClassB>0</MyClassB></MyClass>
}

Edit : Oui, c'est une fonctionnalité documentée. Voir l'entrée MSDN pour XmlSerializer

Une autre option consiste à utiliser un modèle spécial pour créer un champ booléen reconnu par XmlSerializer et à appliquer l'attribut XmlIgnoreAttribute au champ. Le modèle est créé sous la forme de propertyNameSpecified. Par exemple, s'il existe un champ nommé "MyFirstName", vous créerez également un champ nommé "MyFirstNameSpecified" qui indique à XmlSerializer s'il doit générer l'élément XML nommé "MyFirstName".

53
Samuel

Étendre la réponse de Samuel et le commentaire de Greg Beech au cas d'une propriété booléenne: si la propriété est de type bool, vous ne pouvez pas écrire un simple test dans la propriété propertySpecified.

Une solution consiste à utiliser un type Nullable <bool>. Le test dans la propriété propertySpecified est simplement property.HasValue. par exemple.

using System.Xml.Serialization;

public class Person
{
    public bool? Employed { get; set; }

    [XmlIgnore]
    public bool EmployedSpecified { get { return Employed.HasValue; } }
}

Une alternative à l'utilisation d'un type nullable pour une propriété numérique (suggérée par Greg Beech) consiste à définir la propriété value sur une valeur par défaut non valide, telle que -1, comme suit:

using System.ComponentModel;
using System.Xml.Serialization;

public class Person
{
    [DefaultValue(-1)]
    public int Age { get; set; }

    [XmlIgnore]
    public bool AgeSpecified { get { return Age >= 0; } }
}
13
jumpalongjim

Vous pouvez utiliserXmlElementAttribute.IsNullable:

[Serializable]
public class MyClass
{
    [XmlElement(IsNullable = true)]
    public int? Age { get; set; }

    public int MyClassB { get; set; }
}
4
Yochai Timmer

Cela devrait aider Make Age int? et..

public bool ShouldSerializeAge() { return Age.HasValue; }

..cela signifie ajouter les méthodes ShouldSerializeXXX à votre classe!

2
Dog Ears

Oubliez Nullable ... ShouldSerializeXXX est une jolie solution. Ici, l'âge sera sérialisé en fonction de votre état.

[Serializable]
public class MyClass
{
    public int Age { get; set; }
    public int MyClassB { get; set; }

    #region Conditional Serialization
    public bool ShouldSerializeAge() { return age > 0; }
    #endregion
}

[Serializable]
public class MyClassB
{
    public int RandomNumber { get; set; }
}
0
hoang

xsd.exe générera automatiquement la propriété XXXSpecified et ses accesseurs si vous définissez l'attribut 'minoccurs' comme 'minoccurs = "0"' pour un élément ... si vous utilisez un schéma pour définir votre classe xml/class

0
JustAsItSounds