Como posso obter XmlSerializer para bools codificar como sim / não?
-
05-07-2019 - |
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.
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 bool
s que encontra a YesNo
s 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.