Domanda

Sto inviando xml a un altro programma, che prevede flag booleani come " si " o "no", anziché "vero" o " false " ;.

Ho una classe definita come:

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

Quando lo serializzo, il mio output appare così:

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

Ma vorrei che fosse questo:

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

Posso farlo al momento della serializzazione? Preferirei non dover ricorrere a questo:

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

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

Nota che voglio anche essere in grado di deserializzare di nuovo questi dati.

È stato utile?

Soluzione

Ok, ci sto esaminando ancora un po '. Ecco cosa ho escogitato:

// 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");
    }
}

Quindi cambio la mia classe Foo in questa:

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

Nota che poiché SìNo è implicitamente castabile su bool (e viceversa), puoi comunque farlo:

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

In altre parole, puoi trattarlo come un bool.

E w00t! Serializza a questo:

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

Deserializza anche correttamente.

Probabilmente c'è un modo per convincere il mio XmlSerializer a lanciare automaticamente qualsiasi bool che incontra SìNo mentre procede, ma non l'ho ancora trovato. Chiunque?

Altri suggerimenti

Molto semplice. Usa una proprietà surrogata. Applicare XmlIgnore sulla proprietà effettiva. Il surrogato è una stringa e deve utilizzare l'attributo XmlElement che accetta una sostituzione nome-elemento. Specificare il nome della proprietà effettiva nella sostituzione. La proprietà surrogata si serializza in modo diverso in base al valore della proprietà effettiva. Devi anche fornire un setter per Surrogate e il setter dovrebbe impostare la proprietà effettiva in modo appropriato, per qualunque valore abbia serializzato. In altre parole, deve andare in entrambe le direzioni.

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!"); }
        }

    }

fai clic qui per esempio di fonte compilabile completa .

Rendere serializzato un valore bool come "sì" o " no " cambia il tipo di dati dall'essere un valore booleano. Puoi invece aggiungere una proprietà separata che valuta un valore booleano e restituisce " sì " o " no " come appropriato per il suo tipo di dati? Forse potresti anche forzare "sì" o " no " facendo in modo che il tipo restituito sia un enum che specifica solo quei valori.

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

Potrebbe essere eccessivo, ma potrebbe rispondere alle tue necessità. L'unico motivo per cui faccio apparire un enum per un valore così semplice è che dovresti limitare i valori rispetto a consentire qualsiasi stringa.

Uso il metodo della proprietà, ma invece di verificare se la stringa è uguale a yes o no, preferisco controllare se la stringa inizia con (senza distinzione tra maiuscole e minuscole) " YT1 " ;. Ciò consente al file di contenere true, True, t, T, y, Y, yes, Yes, 1, ecc. Tutto ciò che verrà valutato su true. Mentre posso specificare che false è false, False, f, F, n, N, no, No, 0, ecc., Tutto ciò che non corrisponde al vero viene comunque valutato come falso.

Il tuo esempio di proprietà è probabilmente il modo più semplice per farlo. Se aiuta, credo che non sia necessario renderlo una proprietà pubblica, poiché l'attributo implementa ISerializable sulla classe dietro la schiena. Per abilitare la deserializzazione, dovresti essere in grado di implementare set {Bar = value == " yes " ;; }

@Blorgbeard: Se hai più di una di queste classi Sì, nessuna in una classe di oggetti, assicurati di leggere l'intero elemento.

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");
}

Altrimenti ciò potrebbe causare problemi.

  

Il metodo ReadXml deve ricostituire il tuo oggetto usando le informazioni scritte dal metodo WriteXml.

     

Quando viene chiamato questo metodo, il lettore viene posizionato all'inizio dell'elemento che avvolge le informazioni per il tuo tipo. Cioè, giusto   prima del tag di inizio che indica l'inizio di una serializzazione   oggetto. Quando questo metodo ritorna, deve aver letto l'intero elemento   dall'inizio alla fine, compreso tutto il suo contenuto. non mi piace il   Metodo WriteXml, il framework non gestisce l'elemento wrapper   automaticamente. L'implementazione deve farlo. Inosservanza   queste regole di posizionamento possono far sì che il codice generi runtime imprevisto   eccezioni o dati corrotti.

Quello che devi fare suona più come un problema di visualizzazione. Se la tua applicazione lo consente, ti conviene mantenere il tipo di dati come un valore booleano e visualizzare Sì / No nell'interfaccia utente.

scroll top