2009-03-09 2 views
15

Ich sende XML an ein anderes Programm, das boolesche Flags als "Ja" oder "Nein" anstatt als "Wahr" oder "Falsch" erwartet.Wie kann ich XmlSerializer Bools als Ja/Nein zu codieren?

Ich habe eine Klasse definiert wie:

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

Als ich es serialisiert, sieht meine Ausgabe wie folgt:

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

Aber ich würde es dies sein mag:

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

Kann ich dies zum Zeitpunkt der Serialisierung tun? Ich würde es vorziehen, nicht darauf zurückgreifen zu müssen:

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

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

Bitte beachte, dass ich auch diese Daten wieder zu deserialize der Lage sein will.

Antwort

21

zu implementieren. Ok, ich habe in diesem etwas mehr untersucht.Hier ist, was ich habe kommen mit:

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

Dann habe ich meine Foo Klasse dies zu ändern:

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

Beachten Sie, dass, weil YesNo implizit gießbaren zu bool ist (und umgekehrt), können Sie immer noch tun dies:

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

Mit anderen Worten, Sie können es wie ein Bool behandeln.

Und w00t! Es serialisiert sich dazu:

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

Es deserialisiert auch korrekt.

Es gibt wahrscheinlich eine Möglichkeit, um meine XmlSerializer automatisch alle bool s es trifft auf YesNo s wie es geht - aber ich habe es noch nicht gefunden. Jemand?

+0

Lovely, genau das, was ich gesucht habe. Vielen Dank. –

+0

Sie könnte es tun wie das auch: public enum BoolEnum { [XmlEnum ("no")] Falsch = 0, [XmlEnum ("Ja")] Wahr = 1 } – Sauleil

3

Wenn Sie einen Bool-Wert als "Ja" oder "Nein" serialisieren, ändert sich der Datentyp überhaupt nicht. Können Sie stattdessen eine separate Eigenschaft hinzufügen, die einen booleschen Wert auswertet und für ihren Datentyp "ja" oder "nein" zurückgibt? Vielleicht könnten Sie sogar "Ja" oder "Nein" erzwingen, indem Sie den Rückgabetyp zu einer Aufzählung machen, die nur diese Werte angibt.

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

Das könnte Overkill sein, aber könnte auf Ihre Bedürfnisse antworten. Der einzige Grund, warum ich eine Enum für solch einen einfachen Wert aufrufe, ist, dass Sie die Werte einschränken würden, statt eine beliebige Zeichenfolge zuzulassen.

+0

Wow ... ist "ja" zu einfach oder so? –

+0

Haha - ich weiß. Es ist eine Frage, ob eine alte Zeichenkette einen Zeichenkettendatentyp verwendet oder sie auf bestimmte Werte beschränkt. –

+0

: P. Remember Enum. (Try) Parse() ist case-sensitive .... –

-1

Was ist mit der Implementierung von OnSerializing und OnDeserializing Methoden?

-1

Was Sie tun müssen, klingt mehr wie ein Display-Problem. Wenn Ihre Anwendung dies zulässt, sollten Sie den Datentyp besser als Boolescher Wert beibehalten und in Ihrer Benutzerschnittstelle Ja/Nein anzeigen.

+0

Uhh, er spricht über das Serialisieren zu XML ... –

+1

"Ich sende Xml zu einem anderen Programm" klingt, als ob er das "andere" Programm nicht kontrolliert. – SCdF

7

Sehr einfach. Verwenden Sie eine Ersatzeigenschaft. Wenden Sie XmlIgnore auf die tatsächliche Eigenschaft an. Das Ersatzzeichen ist eine Zeichenfolge und muss das XmlElement-Attribut verwenden, das eine Elementnamenüberschreibung verwendet. Geben Sie den Namen der tatsächlichen Eigenschaft in der Überschreibung an. Die Ersatzeigenschaft wird basierend auf dem Wert der tatsächlichen Eigenschaft anders serialisiert. Sie müssen außerdem einen Setter für das Ersatzzeichen angeben, und der Setter sollte die tatsächliche Eigenschaft für jeden Wert, den es serialisiert, entsprechend festlegen. Mit anderen Worten, es muss in beide Richtungen gehen.

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

    } 

hier klicken für full compilable source example.

+2

Yup, aber das ist das Beispiel, das ich in der Frage gegeben habe .. Ich möchte wissen, ob es eine * bessere * Möglichkeit gibt :) – Blorgbeard

+0

Besser? glaube nicht. – Cheeso

0

Ihr Immobilienbeispiel ist wahrscheinlich der einfachste Weg, wie Sie es tun könnten. Wenn es hilft, glaube ich, dass Sie es nicht zu einer öffentlichen Eigenschaft machen müssen, da das Attribut ISerializable für die Klasse hinter Ihrem Rücken implementiert. Um die Deserialisierung zu aktivieren, sollten Sie in der Lage sein, einfach set { Bar = value == "yes"; }

+0

Es scheint, dass es öffentlich sein muss, es sei denn, ich mache es falsch [tm] .. – Blorgbeard

2

Ich benutze die Eigenschaft Methode, aber anstatt zu überprüfen, ob die Zeichenfolge gleich Ja oder Nein ist, bevorzuge ich zu prüfen, ob die Zeichenfolge mit (Groß-und Kleinschreibung) "YT1" beginnt. Dies erlaubt der Datei, wahr, wahr, t, t, y, y, ja, ja, 1 usw. zu enthalten, was als wahr ausgewertet wird. Während ich angeben kann, dass Falsch Falsch, Falsch, F, F, N, N, Nein, Nein, 0 usw. ist, wird alles, was nicht mit dem Wahr übereinstimmt, immer noch zu Falsch ausgewertet.

-1

@Blorgbeard: Wenn Sie mehr als eine dieser YesNo-Klassen in einer Objektklasse haben, stellen Sie sicher, dass Sie das gesamte Element lesen ( ).

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

Andernfalls kann dies zu Problemen führen.

Die ReadXml-Methode muss Ihr Objekt mithilfe der Informationen neu erstellen, die von der WriteXml-Methode geschrieben wurden.

Wenn diese Methode aufgerufen wird, wird der Leser am Anfang des Elements positioniert, das die Informationen für Ihren Typ umschließt. Das heißt, nur vor dem Start-Tag, das den Anfang eines serialisierten Objekts angibt. Wenn diese Methode zurückkehrt, muss sie das gesamte Element vom Anfang bis zum Ende gelesen haben, einschließlich des gesamten Inhalts. Im Gegensatz zur WriteXml-Methode behandelt das Framework das Wrapper-Element nicht automatisch. Ihre Implementierung muss dies tun. Bei Nichteinhaltung dieser können diese Positionierungsregeln dazu führen, dass der Code unerwartete Laufzeitfehler oder beschädigte Daten generiert.