Pergunta

Estou enviando xml para outro programa, que espera bandeiras boolean como "sim" ou "não", em vez de "verdadeiro" ou "falso".

Eu tenho uma classe definida como:

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

Quando eu serializar-lo, a minha saída se parece com isso:

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

Mas eu gostaria que fosse esta:

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

Posso fazer isso no momento da serialização? Eu preferiria não ter que recorrer a este:

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

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

Note que eu também quero ser capaz de deserialize esses dados de volta novamente.

Foi útil?

Solução

Ok, eu estive olhando para isso um pouco mais. Aqui está o que eu vim acima com:

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

Então eu mudar minha classe Foo a esta:

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

Note que, porque YesNo é implicitamente conversível ao bool (e vice-versa), você ainda pode fazer isso:

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

Em outras palavras, você pode tratá-lo como um bool.

E w00t! Ele serializa a esta:

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

Ele também desserializa corretamente.

Provavelmente há alguma maneira de obter o meu XmlSerializer para lançar automaticamente quaisquer bools que encontra a YesNos como ele vai - mas eu não tê-lo encontrado ainda. Qualquer um?

Outras dicas

simples Muito. Use uma propriedade de aluguel. Aplicar XmlIgnore na propriedade real. O substituto é uma string, e deve usar o atributo XmlElement que leva uma substituição elemento-name. Especifique o nome do propriedade real na substituição. A propriedade substituta Serializa diferente com base no valor da propriedade real. Você também deve fornecer um setter para a Surrogate, eo setter deve definir a propriedade real de forma adequada, por qualquer valor que ele serializado. Em outras palavras ele precisa ir para os dois lados.

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

    }

clique aqui para exemplo fonte compilable completo .

Fazendo um valor serialize bool como "sim" ou "não" muda o tipo de dados de ser um boolean em tudo. Em vez disso, você pode adicionar uma propriedade separada, que avalia um booleano e retorna "sim" ou "não" como apropriado para seu tipo de dados? Talvez você poderia até mesmo forçar "sim" ou "não", fazendo o tipo de retorno ser um enum que só especifica esses valores.

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

Isso pode ser um exagero, mas pode responder a sua necessidade. A única razão de eu trazer um enum para um valor tão simples é que você estaria restringindo os valores vs. permitindo que qualquer string.

Eu uso o método de propriedade, mas em vez de verificação para ver se a string é igual a sim ou não, eu prefiro a verificar se a string começa com (case insensitive) "YT1". Isso permite que o arquivo para conter verdadeiro, verdadeiro, t, T, y, Y, sim, sim, 1, etc, tudo que irá avaliar como true. Embora eu possa especificar que falso é falso, falso, f, F, N, N, não, não, 0, etc., qualquer coisa que não corresponde aos verdadeiros avalia ainda como falsa.

Seu exemplo propriedade é provavelmente a maneira mais simples que você poderia fazê-lo. Se ajudar, eu acredito que você não precisa para torná-lo uma propriedade pública, uma vez que os instrumentos de atributos ISerializable na classe atrás de suas costas. Para habilitar a desserialização, você deve ser capaz de simplesmente implementar set { Bar = value == "yes"; }

@Blorgbeard: Se você tiver mais de uma dessas classes YesNo em uma classe de objeto, certifique-se de ler o elemento inteiro.

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

Caso contrário, este problema pode causar.

O método ReadXml deve reconstituir o objeto usando as informações que foi escrito pelo método WriteXml.

Quando este método é chamado, o leitor está posicionado no início do elemento que envolve as informações para o seu tipo. Ou seja, apenas antes da tag de início que indica o início de um serializado objeto. Quando esse método retorna, ele deve ter lido o elemento inteiro do começo ao fim, incluindo todo o seu conteúdo. ao contrário do método WriteXml, o quadro não lidar com o elemento envoltório automaticamente. Sua implementação deve fazê-lo. Deixar de observar estas regras de posicionamento pode causar código para gerar runtime inesperada exceções ou dados corrompidos.

O que você está precisando fazer sons mais como um problema de exibição. Se o seu aplicativo permite, você será melhor fora de manter o tipo de dados como um boolean e exibir Sim / Não em sua interface de usuário.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top