Reputation: 132
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
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