Сериализация Xml против "True" и " False"
у меня проблема с десериализацией XML-файла с логическими значениями. Исходные XML-файлы, которые я десериализую, были созданы из приложения VB6, где все логические значения капитализируются (True
, False
). Когда я пытаюсь десериализовать XML, я получаю
System.FormatException: The string 'False' is not a valid Boolean value.
есть ли способ сказать игнорировать случай с атрибутом?
9 ответов
вы можете прочитать это значение как строку в строковое поле, а затем иметь поле только для чтения bool, в котором есть оператор if для возврата bool true или false.
например (используя c#):
public bool str2bool(string str)
{
if (str.Trim().ToUpper() == "TRUE")
return true;
else
return false;
}
и вы можете использовать его в шаблоне:
<xsl:if test="user:str2bool($mystr)">
на основе еще один вопрос переполнения стека вы можете сделать:
public class MySerilizedObject
{
[XmlIgnore]
public bool BadBoolField { get; set; }
[XmlElement("BadBoolField")]
public string BadBoolFieldSerialize
{
get { return this.BadBoolField ? "True" : "False"; }
set
{
if(value.Equals("True"))
this.BadBoolField = true;
else if(value.Equals("False"))
this.BadBoolField = false;
else
this.BadBoolField = XmlConvert.ToBoolean(value);
}
}
}
нет. Сериализатор XML работает со схемой XML, а" True "и" False " не являются допустимыми логическими значениями.
вы можете либо использовать преобразование XML для преобразования этих двух значений, либо реализовать интерфейс IXmlSerializable и выполнить сериализацию и десериализацию самостоятельно.
Я не думаю, что есть. Вы можете сделать его string и сделать сравнение (строка.Сравните) установив значение ignoreCase в true.
я наткнулся на ту же проблему и, основываясь на ответе jman, решил ее следующим образом:
[XmlIgnore]
public bool BadBoolField { get; set; }
[XmlAttribute("badBoolField")]
public string BadBoolFieldSerializable
{
get
{
return this.BadBoolField.ToString();
}
set
{
this.BadBoolField= Convert.ToBoolean(value);
}
}
имейте в виду, что это не обязательно спецификация XML / сериализации, но она работает хорошо, и она может обрабатывать широко распространенные значения преобразования (т. е. строку, такую как "True", "true", если вы замените ограничение быть строкой, она также может обрабатывать числа).
вот гораздо более чистое решение, которое я придумал на основе некоторых других вопросов, которые я нашел. Это намного чище, потому что тогда вам не нужно ничего в вашем коде, кроме объявления типа как SafeBool, например:
public class MyXMLClass
{
public SafeBool Bool { get; set; }
public SafeBool? OptionalBool { get; set; }
}
вы даже можете сделать их необязательными, и все это просто работает. Эта структура SafeBool будет обрабатывать любые варианты вариантов true/false, yes/no или y/n. Он всегда будет сериализован как true/ false, однако у меня есть другие структуры, подобные этому, которые я использую для сериализации конкретно как y/n или yes / no, когда этого требует схема (т. е. BoolYN, BoolYesNo structs).
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace AMain.CommonScaffold
{
public struct SafeBool : IXmlSerializable
{
private bool _value;
/// <summary>
/// Allow implicit cast to a real bool
/// </summary>
/// <param name="yn">Value to cast to bool</param>
public static implicit operator bool(
SafeBool yn)
{
return yn._value;
}
/// <summary>
/// Allow implicit cast from a real bool
/// </summary>
/// <param name="b">Value to cash to y/n</param>
public static implicit operator SafeBool(
bool b)
{
return new SafeBool { _value = b };
}
/// <summary>
/// This is not used
/// </summary>
public XmlSchema GetSchema()
{
return null;
}
/// <summary>
/// Reads a value from XML
/// </summary>
/// <param name="reader">XML reader to read</param>
public void ReadXml(
XmlReader reader)
{
var s = reader.ReadElementContentAsString().ToLowerInvariant();
_value = s == "true" || s == "yes" || s == "y";
}
/// <summary>
/// Writes the value to XML
/// </summary>
/// <param name="writer">XML writer to write to</param>
public void WriteXml(
XmlWriter writer)
{
writer.WriteString(_value ? "true" : "false");
}
}
}
существует невероятно простое и короткое решение в частном случае.
сегодня я столкнулся с аналогичной проблемой с внешним XML-файлом, который содержит значения TRUE/FALSE, которые должны иметь логическое значение.
Если для приложения не обязательно, что десериализованный документ содержит собственный bool, но это просто десериализация его до чего-то, что ограничено любыми двумя альтернативными значениями, то можно просто использовать перечисление (здесь для атрибута в качестве примера):
public enum BOOL {FALSE, TRUE};
public MyClass
{
[XmlAttribute]
public BOOL MyStrangeBooleanAttribute {get; set;}
}
Это просто десериализуется без каких-либо проблем из такого элемента, как этот
<MyClass MyStrangeBooleanAttribute = "TRUE" />
конечно, невозможно использовать свойство в коде для прямых булевых операций, таких как
if (MyStrangeBooleanAttribute) // ... doesn't work
Я думаю, что, вероятно, можно было бы справиться с этим, определив неявное преобразование, но я не тестировал его, потому что он мне не нужен.
Не беспокойтесь об исправлении сломанной системы xml или борьбе с XmlSerializer, особенно для чего-то настолько тривиального. Оно того не стоит. VB6 не вернется в ближайшее время.
вместо этого получите документ до его десериализации и измените значения. Если вы беспокоитесь об изменении их вне тегов, используйте регулярные выражения или включите угловые скобки в значения.
xml = xml.Replace("True", "true").Replace("False", "false");
Он не выиграет никаких наград за элегантность, но он вернет вас работать. Иногда нужно просто надеть синий воротничок.
что касается производительности, да, вы повторяете через строку O(n), но поскольку строки замены имеют одинаковую длину, для этого не требуется никаких движущихся строковых элементов. Кроме того, в зависимости от реализации могут возникнуть большие накладные расходы при изменении XmlSerializer.