Xml Serialization против "True" и "False"

У меня возникла проблема с десериализацией XML файла с булевыми значениями. Исходные файлы XML, которые я десериализую, были созданы из приложения VB6, где все логические значения капитализируются (True, False). Когда я пытаюсь десериализовать XML, я получаю

System.FormatException: The string 'False' is not a valid Boolean value.

Есть ли способ сказать игнорировать регистр с атрибутом?

Ответ 1

Вы можете прочитать это значение как строку в поле строки, а затем иметь поле readoolly bool, в котором в нем есть оператор if, чтобы вернуть bool true или false.

Например (с помощью С#):

public bool str2bool(string str)
{
  if (str.Trim().ToUpper() == "TRUE")
      return true;
  else
      return false;
}

И вы можете использовать его в шаблоне:

<xsl:if test="user:str2bool($mystr)">

Ответ 2

Вместо использования True или False используйте 0 или 1. Он будет работать для Boolean.

Ответ 3

На основе другого вопроса о переполнении стека вы можете:

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

Ответ 4

Нет. Сериализатор XML работает с XML-схемой, а "True" и "False" не являются допустимыми булевыми.

Вы можете использовать XML Transform для преобразования этих двух значений, или вы можете реализовать интерфейс IXmlSerializable и выполнить сериализацию и десериализацию самостоятельно.

Ответ 5

Я не думаю, что есть. Вы можете сделать это строкой и выполнить сравнение (String.Compare), установив для параметра ignoreCase значение true.

Ответ 6

Я наткнулся на ту же проблему, и, основываясь на ответе jman, я решил это так:

    [XmlIgnore]
    public bool BadBoolField { get; set; }

    [XmlAttribute("badBoolField")]
    public string BadBoolFieldSerializable
    {
        get
        {
            return this.BadBoolField.ToString();
        }
        set
        {
            this.BadBoolField= Convert.ToBoolean(value);
        }
    }

Помните, что это не обязательно по спецификации XML/Serialization, но она работает хорошо и может обрабатывать широко распространенные значения преобразования (например, строки типа "True", "true", если вы замените ограничение на строку может также обрабатывать числа).

Ответ 7

Вот гораздо более чистое решение, которое я придумал, основываясь на некоторых других вопросах, которые я нашел. Это намного чище, потому что тогда вам не нужно ничего в коде, кроме объявления типа 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");
        }
    }
}

Ответ 8

В частном случае существует невероятно простое и короткое решение.

Сегодня я столкнулся с аналогичной проблемой, с внешним 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

Я думаю, что, возможно, с этим можно было бы справиться, указав неявное преобразование, но я его не тестировал, потому что мне это не нужно.

Ответ 9

У меня есть xml с большим количеством логических элементов, и я не хочу, чтобы у меня было так много повторяющихся логических свойств, поэтому я попробовал другой подход к предоставлению пользовательского xml-читателя для выполнения этой работы:

public class MyXmlReader : XmlTextReader
{
    public MyXmlReader(TextReader reader) : base(reader) { }
    public override string ReadElementString()
    {
        var text = base.ReadElementString();

        // bool TryParse accepts case-insensitive 'true' and 'false'
        if (bool.TryParse(text, out bool result))
        {
            text = XmlConvert.ToString(result);
        }

        return text;
    }
}

и использовать с:

using (var sr = new StringReader(text))
using (var r = new MyXmlReader(sr))
{
    var result = serializer.Deserialize(r);
}

Ответ 10

Не утруждайте себя исправлением неисправной xml-системы или сражающейся с XmlSerializer, особенно для чего-то такого тривиального. Это не стоит. VB6 не вернется в ближайшее время.

Вместо этого возьмите документ перед его десериализацией и измените значения. Если вы беспокоитесь об изменении их вне тегов, используйте регулярные выражения или включите угловые скобки в значениях.

    xml = xml.Replace("True", "true").Replace("False", "false");

Он не собирается выигрывать награды за элегантность, но он возвращает вас к работе. Иногда вам просто нужно иметь синий воротник.

Что касается производительности, да, вы повторяете строку O (n), но поскольку строки замены имеют одинаковую длину, для нее не требуются элементы движущейся строки. Более того, в зависимости от реализации, в модификации XmlSerializer могут быть большие накладные расходы.