Xml Serialization vs. “True” and “False”

一曲冷凌霜 提交于 2019-12-30 08:06:05

问题


I'm having an issue with deserializing an XML file with boolean values. The source XML files I'm deserializing were created from a VB6 app, where all boolean values are capitalized (True, False). When I try to deserialize the XML, I'm getting a

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

Is there a way to say ignore case with an attribute?


回答1:


You could read that value as a string into a string field, then have a readonly bool field that had an if statement in it to return bool true or false.

For example (using c#):

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

And you can use it in the template:

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



回答2:


Based on another stack overflow question you can do:

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



回答3:


Instead of using True or False, use 0 or 1. It will work for Boolean.




回答4:


Here is a much cleaner solution I came up with based on some other questions I found. It is much cleaner because then you don't need to anything in your code except declare the type as SafeBool, like this:

public class MyXMLClass
{
    public SafeBool Bool { get; set; }
    public SafeBool? OptionalBool { get; set; }
}

you can even make them optional and it all just works. This SafeBool struct will handle any case variants of true/false, yes/no or y/n. It will always serialize as true/false, however I have other structs similar to this I use to serialize specifically as y/n or yes/no when the schema requires that (ie: 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");
        }
    }
}



回答5:


There isn't. The XML Serializer works with XML Schema, and "True" and "False" are not valid booleans.

You could either use an XML Transform to convert these two values, or you could implement the IXmlSerializable interface and do the serialization and deserialization on your own.




回答6:


I have an xml with many booleans and I didn't want to end up having so many duplicate boolean properties, so I tried a different approach of providing a custom xml reader to do the work:

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

and use with:

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



回答7:


I dont think there is. You could make it string and do a comparison (String.Compare) by setting the ignoreCase value to true.




回答8:


I stumbled upon the same issue, and based on the answer by jman, I solved it this way:

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

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

Be aware this is not necessarily by XML / Serialization specification, but it works good and it can handle a widespread conversion values (i.e. string like "True", "true", if you would replace the constraint of being a string it could handle numbers as well).




回答9:


There is an incredibly simple and short solution in a special case.

I have encountered a similar problem today, with an externally given XML file that contains the values TRUE/FALSE which are supposed to have boolean meaning.

If it is not mandatory for one's application that the deserialized document contains a native bool, but it's merely about deserializing it to something that is constrained to any two alternative values, then one can simply use an enum (here for an Attribute by way of example):

public enum BOOL {FALSE, TRUE};

public MyClass
{
    [XmlAttribute]
    public BOOL MyStrangeBooleanAttribute {get; set;}
}

This will just deserialize without any problem from an Element like this

<MyClass MyStrangeBooleanAttribute = "TRUE" />

Of course it is not possible then to use the property in code for direct boolean operations, like

if (MyStrangeBooleanAttribute) // ... doesn't work

I think it could probably be possible to handle this by defining an implicit conversion, but I haven't tested it because I don't need it.




回答10:


Don't bother fixing a broken xml system or fighting XmlSerializer, especially for something so trivial. It's not worth it. VB6 isn't coming back anytime soon.

Instead, get hold of the document before it's deserialized and change the values. If you're worried about changing them outside the tags, then use regular expressions or include the angle brackets in the values.

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

It's not going to win any awards for elegance, but it gets you back to work. Sometimes you just have to blue collar it.

As for performance, yes, you are reiterating through the string O(n), but since the replacement strings are the same length, it doesn't require any moving string elements around. Moreover, depending on the implementation, there might be a greater overhead in modifying XmlSerializer.



来源:https://stackoverflow.com/questions/1155227/xml-serialization-vs-true-and-false

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!