Dela via


Workaround to deserialize ‘True’, ‘False’ using XmlSerializer

This is the scenario, system A is taking an xml from system B. System A will deserialize the xml into an object. Unfortunately, a dev in system B decides that System.Xml namespace is evil and construct the xml using StringBuilder. In one of the element, the dev call ToString() method of a boolean variable. The result? the xml contains ‘True’ or ‘False’. System A takes the input and it throws exceptions with error message “The string 'False' is not a valid Boolean value”, or “The string 'True' is not a valid Boolean value'”.

W3C defines boolean data type as ‘true’, ‘false’, ‘0’, and ‘1’. So this behavior is expected.

There is a way to workaround this, only under this scenario, you want to keep the type safety of your object, and you have the code. The steps are:

  • Decorate the boolean property with XmlIgnore.
  • Inherit your class and add a string property, decorate it with XmlElement. Set the element name to match the xml node name.
  • Add code on the new property on the descendant class, to make the property in the descendant class work as an adapter for the actual boolean property.
 [XmlRoot(ElementName = "data", Namespace = "https://contoso.com/")]
[XmlInclude(typeof(MyClassXmlAdapter))]
public class MyClass
{
    [XmlIgnore]
    public bool BooleanField { get; set; }
}

[XmlRoot(ElementName = "data", Namespace = "https://contoso.com/")]
public sealed class MyClassXmlAdapter : MyClass
{
    [XmlElement(ElementName = "Test")]
    public string BooleanAsString
    {
        get
        {
            return XmlConvert.ToString(BooleanField);
        }

        set
        {
            bool ParsedValue;

            if (!Boolean.TryParse(value, out ParsedValue))
                ParsedValue = XmlConvert.ToBoolean(value);

            BooleanField = ParsedValue;
        }
    }
}

In your serialization code, create the XmlSerializer using MyClassXmlAdapter as type, and the serializer will serialize and deserialize to MyClass with no problem. All of your code that works with MyClass won’t have to be changed.

If you don’t have access to the code, or the class is sealed, it is much more difficult. The only way to do it is by using a helper class. In this sample below, MyClass is sealed and you have only the assembly. Then the solution is to create another class that have almost identical fields with MyClass, with the exception the boolean field.

 [XmlRoot(ElementName = "data", Namespace = "https://contoso.com/")]
public sealed class MyClass
{
    [XmlElement]
    public bool BooleanField { get; set; }
}

[XmlRoot(ElementName = "data", Namespace = "https://contoso.com/")]
public sealed class MyClassXmlAdapter
{
    private bool _BooleanField;

    [XmlElement(ElementName = "Test")]
    public string BooleanAsString
    {
        get
        {
            return XmlConvert.ToString(_BooleanField);
        }

        set
        {
            bool ParsedValue;

            if (!Boolean.TryParse(value, out ParsedValue))
                ParsedValue = XmlConvert.ToBoolean(value);

            _BooleanField = ParsedValue;
        }
    }

    public void CopyTo(MyClass target)
    {
        target.BooleanField = this.BooleanField;
    }
}

After deserializing the xml with ‘True’ or ‘False’, call CopyTo().

These solutions are not ideal, but at least, these are better options rather than cleaning up the Xml.

Comments

  • Anonymous
    May 21, 2011
    Amazingly, I got this exception, too. Thanks for posting this!

  • Anonymous
    March 21, 2012
    Why wouldn't the XmlSerializer simply use Boolean.TryParse instead of failing to parse "False" or "True" as a valid boolean value?  Seems to strict in my eyes.

  • Anonymous
    March 21, 2012
    Justin, I am not part of the team, I am not entirely sure. But if I can make a guess, the Xml specification says that the legal representation of boolean values are true, false, 1, 0. www.w3.org/.../xmlschema-2 If you serialize an object using XmlSerializer, it will be lower case true/false. So I think, the team wants to conform with the Xml standard.