2009-03-09 9 views
15

Estoy enviando xml a otro programa, que espera que los indicadores booleanos sean "sí" o "no", en lugar de "verdadero" o "falso".¿Cómo puedo obtener XmlSerializer para codificar bools como yes/no?

He una clase definida como:

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

Cuando serializarlo, mi producción se ve así:

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

Pero me gustaría que fuera la siguiente:

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

¿Puedo hacer esto en el momento de la serialización? Yo preferiría no tener que recurrir a esto:

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

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

Tenga en cuenta que también quiero ser capaz de deserializar estos datos de nuevo otra vez.

Respuesta

21

Ok, he estado investigando esto un poco más.Esto es lo que he llegado con:

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

Luego de que cambie de clase Foo a esto:

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

Tenga en cuenta que debido a YesNo es implícitamente moldeable a bool (y viceversa), todavía se puede haz esto:

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

En otras palabras, puedes tratarlo como un bool.

Y w00t! Se serializa a esto:

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

También deserializa correctamente.

Probablemente haya alguna forma de hacer que mi XmlSerializer emita automáticamente cualquier bool s que encuentre en YesNo s, pero todavía no lo he encontrado. ¿Nadie?

+0

Encantador, justo lo que estaba buscando. Gracias. –

+0

que podría hacerlo como eso también: enumeración pública BoolEnum { [XmlEnum ("no")] Falso = 0, [XmlEnum ("sí")] True = 1} – Sauleil

3

Hacer que un valor bool se serialice como "sí" o "no" cambia el tipo de datos de ser un booleano en absoluto. En su lugar, ¿puede agregar una propiedad separada que evalúe un booleano y devuelva "sí" o "no" según corresponda para su tipo de datos? Tal vez incluso podría forzar "sí" o "no" al hacer que el tipo de devolución sea una enumeración que solo especifique esos valores.

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

Eso podría ser exagerado, pero podría responder a su necesidad. La única razón por la que menciono una enumeración por un valor tan simple es que estaría restringiendo los valores y permitiendo cualquier cadena.

+0

Wow ... es "sí" es demasiado simple o algo así? –

+0

Haha - lo sé. Se trata de permitir que cualquier cadena antigua use un tipo de datos de cadena o restringirla a valores particulares. –

+0

: P. Recuerda Enum. (Try) Parse() distingue entre mayúsculas y minúsculas .... –

-1

¿qué ocurre al implementar métodos OnSerializing y OnDeserializing?

-1

Lo que necesita hacer suena más como un problema de visualización. Si su aplicación lo permite, será mejor que mantenga el tipo de datos como booleano y que muestre Sí/No en su interfaz de usuario.

+0

Uhh, él está hablando de serializar en XML ... –

+1

"Estoy enviando xml a otro programa" parece que no controla el "otro" programa. – SCdF

7

Muy simple. Use una propiedad sustituta. Aplicar XmlIgnore en la propiedad real. El suplente es una cadena y debe usar el atributo XmlElement que toma una anulación de nombre de elemento. Especifique el nombre de la propiedad real en la anulación. La propiedad sustituta se serializa de manera diferente en función del valor de la propiedad real. También debe proporcionar un colocador para el sustituto, y el colocador debe establecer la propiedad real de manera apropiada, independientemente del valor que serialice. En otras palabras, necesita ir en ambos sentidos.

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

    } 

clic aquí para full compilable source example.

+2

Sí, pero ese es el ejemplo que di en la pregunta .. Quiero saber si hay una * mejor * forma :) – Blorgbeard

+0

¿Mejor? no lo creo – Cheeso

0

El ejemplo de su propiedad es probablemente la forma más sencilla de hacerlo. Si ayuda, creo que no es necesario que sea una propiedad pública, ya que el atributo implementa ISerializable en la clase a su espalda. Para habilitar la deserialización, debería poder implementar set { Bar = value == "yes"; }

+0

Parece que tiene que ser público, a menos que lo esté haciendo mal [tm] .. – Blorgbeard

2

Utilizo el método de propiedad, pero en lugar de verificar si la cadena es igual a sí o no, prefiero verificar si la cadena comienza con (sin distinción entre mayúsculas y minúsculas) "YT1". Esto permite que el archivo contenga verdadero, verdadero, t, t, y, y, sí, sí, 1, etc. todo lo cual se evaluará como verdadero. Si bien puedo especificar que false es falso, False, f, F, n, N, no, No, 0, etc., cualquier elemento que no coincida con el valor verdadero todavía se evalúa como falso.

-1

@Blorgbeard: Si tiene más de una de estas clases YesNo en una clase de objeto, , asegúrese de leer todo el 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"); 
} 

De lo contrario, esto podría causar problemas.

El método ReadXml debe reconstituir su objeto utilizando la información escrita por el método WriteXml.

Cuando se invoca este método, el lector se coloca al comienzo del elemento que envuelve la información para su tipo. Es decir, solo antes de la etiqueta de inicio que indica el comienzo de un objeto serializado . Cuando este método retorna, debe haber leído todo el elemento de principio a fin, incluidos todos sus contenidos. A diferencia del método WriteXml, el marco no maneja el elemento contenedor automáticamente. Su implementación debe hacerlo. No observar estas reglas de posicionamiento pueden hacer que el código genere excepciones inesperadas en el tiempo de ejecución o datos corruptos.

Cuestiones relacionadas