Handle Empty String as Null in XML Deserialization
I have the same problem with regard to this question. I am sending data back and forth with a SOAP-based API where the responses don't follow the standard quite right, specifically with null values. For DateTime
, the API will send back an empty string, like this:
<nextreview></nextreview>
Which causes the following error to occur on deserialization:
The string '' is not a valid AllXsd value.
So my thought was to create a custom Nullable type, NullableOrEmpty<T>
, implementing IXMLSerializable
that handles empty string by converting it to null. The problem is I only want to handle the exceptional case of an empty string. Everything else I want to serialize and deserialize as normal, using the 'default' behavior. How do I simulate the default behavior of serialization in my code below?
public class NullableOrEmpty<T> : IXmlSerializable
where T : struct
{
public T? NullableValue { get; set; }
public T Value { get { return this.NullableValue.Value; } }
public bool HasValue { get { return this.NullableValue.HasValue; } }
...
public void ReadXml(XmlReader reader)
{
string xml = reader.ReadElementContentAsString();
if (string.IsNullOrEmpty(xml))
{
this.NullableValue = null;
}
else
{
//THIS SHOULD DO THE DEFAULT. THIS DOESN'T WORK. WHAT DO I DO??
//this.NullableValue = (T?)new XmlSerializer(typeof(T?)).Deserialize(reader);
}
}
public void WriteXml(XmlWriter writer)
{
//THIS SHOULD DO THE DEFAULT. THIS DOESN'T WORK. WHAT DO I DO??
//new XmlSerializer(typeof(T?)).Serialize(writer, this.NullableValue);
}
}
When I say "THIS DOESN'T WORK", it specifically generates the following error message, probably because it's trying to consume something that isn't there:
There is an error in XML document (63, 6).
<lastreview xmlns=''>
was not expected.
Here is a snippet of XML at that spot. The error is caused by the value in birthdate
, because I'm not consuming it correctly in the non-exceptional case where the value is actually given:
<udf4></udf4>
<udf3></udf3>
<birthdate>1978-05-24Z</birthdate>
<lastreview></lastreview>
<fulltim开发者_如何学Ce>1</fulltime>
Any thoughts or ideas are appreciated. I can post more code samples if needed or test out suggestions. Thanks!
One thing you can do here, though it may be more of a pain is to implement the adapter pattern, where the object you populate from the xml result just has properties of type string, then write a converter method to populate your 'real' object checking for empty strings when the destination property is DateTime. It may be easier than implementing your own serializer.
I am no longer using this class (I am requiring validations by the 3rd party instead), but I was actually able to get it to working by manually handling all the data types using the XmlConvert
helpers:
public void ReadXml(XmlReader reader)
{
string xml = reader.ReadElementContentAsString();
if (string.IsNullOrEmpty(xml))
{
this.NullableValue = null;
}
else
{
if (this.NullableValue is bool)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToBoolean(xml), typeof(T?));
else if (this.NullableValue is byte)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToByte(xml), typeof(T?));
else if (this.NullableValue is char)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToChar(xml), typeof(T?));
else if (this.NullableValue is DateTime)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToDateTime(xml), typeof(T?));
else if (this.NullableValue is decimal)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToDecimal(xml), typeof(T?));
else if (this.NullableValue is double)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToDouble(xml), typeof(T?));
else if (this.NullableValue is Guid)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToGuid(xml), typeof(T?));
else if (this.NullableValue is short)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToInt16(xml), typeof(T?));
else if (this.NullableValue is int)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToInt32(xml), typeof(T?));
else if (this.NullableValue is long)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToInt64(xml), typeof(T?));
else if (this.NullableValue is float)
this.NullableValue = (T?)Convert.ChangeType(XmlConvert.ToSingle(xml), typeof(T?));
}
}
public void WriteXml(XmlWriter writer)
{
new XmlSerializer(typeof(T?)).Serialize(writer, this.NullableValue);
}
精彩评论