Comment puis-je faire en sorte que XmlSerializer code les bools comme étant oui / non?

StackOverflow https://stackoverflow.com/questions/625091

  •  05-07-2019
  •  | 
  •  

Question

J'envoie un fichier XML à un autre programme, qui attend des indicateurs booléens sous la forme "oui". ou "non", plutôt que "vrai" ou "faux".

J'ai une classe définie comme:

[XmlRoot()]
public class Foo {
    public bool Bar { get; set; }
}

Lorsque je le sérialise, ma sortie ressemble à ceci:

<Foo><Bar>true</Bar></Foo>

Mais j'aimerais que ce soit ceci:

<Foo><Bar>yes</Bar></Foo>

Puis-je le faire au moment de la sérialisation? Je préférerais ne pas avoir à recourir à ceci:

[XmlRoot()]
public class Foo {
    [XmlIgnore()]
    public bool Bar { get; set; }

    [XmlElement("Bar")]
    public string BarXml { get { return (Bar) ? "yes" : "no"; } }
}

Notez que je souhaite également pouvoir désérialiser ces données.

Était-ce utile?

La solution

Ok, je me suis penché sur la question de plus près. Voici ce que j'ai trouvé:

// use this instead of a bool, and it will serialize to "yes" or "no"
// minimal example, not very robust
public struct YesNo : IXmlSerializable {

    // we're just wrapping a bool
    private bool Value;

    // allow implicit casts to/from bool
    public static implicit operator bool(YesNo yn) {
        return yn.Value;
    }
    public static implicit operator YesNo(bool b) {
        return new YesNo() {Value = b};
    }

    // implement IXmlSerializable
    public XmlSchema GetSchema() { return null; }
    public void ReadXml(XmlReader reader) {
        Value = (reader.ReadElementContentAsString() == "yes");
    }
    public void WriteXml(XmlWriter writer) {
        writer.WriteString((Value) ? "yes" : "no");
    }
}

Ensuite, je change ma classe Foo en ceci:

[XmlRoot()]
public class Foo {      
    public YesNo Bar { get; set; }
}

Notez que, comme YesNo peut être converti implicitement en bool (et vice versa), vous pouvez toujours le faire:

Foo foo = new Foo() { Bar = true; };
if ( foo.Bar ) {
   // ... etc

En d'autres termes, vous pouvez le traiter comme un bool.

Et w00t! Il sérialise à ceci:

<Foo><Bar>yes</Bar></Foo>

Il se désérialise également correctement.

Il existe probablement un moyen de faire en sorte que mon XmlSerializer transforme automatiquement tous les bool rencontrés avec YesNo tels quels, mais je ne les ai pas encore trouvés. Quelqu'un?

Autres conseils

Très simple. Utilisez une propriété de substitution. Appliquez XmlIgnore sur la propriété réelle. Le substitut est une chaîne et doit utiliser l'attribut XmlElement qui prend une substitution de nom d'élément. Indiquez le nom de la propriété actuelle dans le remplacement. La propriété de substitution est sérialisée différemment en fonction de la valeur de la propriété réelle. Vous devez également fournir un objet de définition pour le substitut, qui doit définir la propriété réelle de manière appropriée, quelle que soit la valeur sérialisée. En d'autres termes, il faut aller dans les deux sens.

Snip:

    public class SomeType 
    {

        [XmlElement]
        public int IntValue;

        [XmlIgnore]
        public bool Value;

        [XmlElement("Value")]
        public string Value_Surrogate {
            get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; }
            set { Value= (value=="Yes, definitely!"); }
        }

    }

cliquez ici pour exemple de source compilable complet .

Création d'une valeur booléenne sérialisée sous la forme " yes " ou " no " modifie le type de données d'être un booléen du tout. Au lieu de cela, pouvez-vous ajouter une propriété distincte qui évalue un booléen et retourne "oui" ou " no " comme approprié pour son type de données? Peut-être pourriez-vous même forcer " oui " ou " no " en faisant que le type de retour soit une énumération qui spécifie uniquement ces valeurs.

public YesOrNo DoYouLoveIt
{
    get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; }
}

C'est peut-être exagéré, mais cela pourrait répondre à votre besoin. La seule raison pour laquelle je mentionne une énumération pour une valeur aussi simple est que vous limitez les valeurs au lieu de permettre toute chaîne.

J'utilise la méthode de la propriété, mais au lieu de vérifier si la chaîne est égale à oui ou non, je préfère vérifier si la chaîne commence par (insensible à la casse) "YT1". Cela permet au fichier de contenir true, True, t, T, y, Y, oui, oui, 1, etc., ce qui sera évalué à true. Bien que je puisse spécifier que false est false, False, f, F, n, N, non, non, 0, etc., tout ce qui ne correspond pas à la valeur true est évalué à false.

Votre exemple de propriété est probablement le moyen le plus simple de le faire. Si cela vous aide, je pense qu'il n'est pas nécessaire d'en faire une propriété publique, car l'attribut implémente ISerializable sur la classe derrière votre dos. Pour activer la désérialisation, vous devriez pouvoir simplement mettre en œuvre , définissez {Bar = value == " yes " ;; }

@Blorgbeard: Si vous avez plus d'une de ces classes YesNo dans une classe d'objet, assurez-vous de lire l'élément entier.

public void ReadXml(XmlReader reader)
{
    string element = reader.ReadOuterXml();
    int startIndex = element.IndexOf('>') + 1;
    int length = element.LastIndexOf('<') - startIndex;

    string text = (element.Substring(startIndex, length).ToLowerInvariant();

    Value = (text == "yes");
}

Sinon, cela pourrait causer des problèmes.

  

La méthode ReadXml doit reconstituer votre objet en utilisant les informations écrites par la méthode WriteXml.

     

Lorsque cette méthode est appelée, le lecteur est positionné au début de l’élément qui encapsule les informations relatives à votre type. C'est juste   avant la balise de début qui indique le début d'un numéro de série   objet. Quand cette méthode retourne, il doit avoir lu la totalité de l'élément   du début à la fin, y compris tout son contenu. Contrairement à la   WriteXml, le framework ne gère pas l’élément wrapper   automatiquement Votre implémentation doit le faire. Omettre d'observer   ces règles de positionnement peuvent provoquer le code à générer une exécution inattendue   exceptions ou données corrompues.

Ce que vous devez faire ressemble davantage à un problème d’affichage. Si votre application le permet, conservez le type de données booléen et affichez Oui / Non dans votre interface utilisateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top