Stuie_M
Stuie_M

Reputation: 132

c# the boolean serialization issue

I have a value I'd like to deserialize as a boolean, but the deserialisation doesn't support the case given, which is: FALSE or TRUE, and the format supplied would be cumbersome to change, I get an exception thrown:

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

This I know is because the XML serialiser doesn't support this; only valid XML schema values such as 'false' or 'true' are allowed (1st bit of research, check!).

So, the first alternative is to create a string property to do the conversion, something like this:

public class MyExample
{
    [XmlIgnore] public bool _booleanField { get; set; }

    [XmlElement("BooleanField")]
    public string BooleanFieldString
    {
        get => _booleanField.ToString().ToLower();
        set => _booleanField = ConvertBooleanStringValue(value);
    }

    private bool ConvertBooleanStringValue(string booleanAsString)
    {
        switch (booleanAsString.ToUpper())
        {
            case "TRUE":
            case "T":
            case "1":
            case "Y":
            case "YES":
                return true;
            case "FALSE":
            case "F":
            case "0":
            case "N":
            case "NO":
                return false;
            default:
                return false;
        }
    }
}

But I don't like this because it messes up the clean classes I have built and would need to pepper this around all the boolean values I have across 28 classes (2nd bit of research, check!).

The XML I receive has the potential for a lot of boolean values to be passed, for all sorts of parameters, so if I chose to parsing the source data I'd need to know which all the boolean elements are. The limitation here is the XMLSerialiser, although it's not really its fault, I get that.

I could implement the ISerializable interface and write a specific implementation, but that's a lot of work for a boolean value, and from the research I've done I'm not convinced there's a way to do this for a specific property only which would obviously limit the pain (3rd bit of research!!!).

There are other serialisation frameworks, that may resolve this like ExtendedXmlSerializer, but I'd rather stick with what I know, if possible.

Upvotes: 4

Views: 2722

Answers (1)

pfx
pfx

Reputation: 23294

With the prerequisite that you can identify all boolean elements that need this alternative parsing routine, you can implement a custom XmlTextReader and pass that to the regular XmlSerializer.

The CustomXmlReader below accepts a list of xml element names that need special care.

public class CustomXmlReader : XmlTextReader
{
    private readonly IList<String> _booleanFieldNames;
    private Boolean _parseBooleanString;

    public CustomXmlReader(IList<String> booleanFieldNames, TextReader reader) : base(reader)
    {
        this._booleanFieldNames = booleanFieldNames;
    }        

    public override XmlNodeType MoveToContent()
    {            
        XmlNodeType nodeType = base.MoveToContent();            
        this._parseBooleanString = ((XmlNodeType.Element == nodeType)
            && this._booleanFieldNames.Contains(this.Name)
            );            
        return nodeType;
    }        

    public override String ReadString()
    {
        String value = base.ReadString();                        
        if (this._parseBooleanString)
        {                
            if (value.Equals("TRUE", StringComparison.OrdinalIgnoreCase)
                || value.Equals("T", StringComparison.OrdinalIgnoreCase)
                || value.Equals("1", StringComparison.OrdinalIgnoreCase)
                || value.Equals("YES", StringComparison.OrdinalIgnoreCase)
                || value.Equals("Y", StringComparison.OrdinalIgnoreCase)
                )
            { 
                return "true";                        
            }

            return "false";                
        }

        return value;
    }
}

The classes corresponding to the xml stay unaware of this custom parsing.

public class MyExample
{
    public MyExample() {}

    [XmlElement("BooleanField")]
    public Boolean BooleanFieldString { get; set; }
}

The code below parses the following xml structure

const String XML = @"
    <MyExample>
        <BooleanField>T</BooleanField>
    </MyExample>";

using (var stringReader = new StringReader(XML))
using (var xmlReader = new CustomXmlReader(new List<String> { "BooleanField" }, stringReader))
{
    XmlSerializer serializer = new XmlSerializer(typeof(MyExample));
    MyExample mx = serializer.Deserialize(xmlReader) as MyExample;
    Console.WriteLine(mx.BooleanFieldString); // True
}

Upvotes: 2

Related Questions