1

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>
<fulltime>1</fulltime>

Any thoughts or ideas are appreciated. I can post more code samples if needed or test out suggestions. Thanks!

1
  • 1
    You can't use the same XmlReader. You've already read past the content you want to read again. Commented May 24, 2011 at 22:01

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your thoughts. I wanted to avoid adding a bunch of logic when it only affects about 5 out of 100 properties. I have already seen the idea of a shim property here: stackoverflow.com/questions/838246/…, which is what I might end up using.
Your link looks like a very good solution. Easy to read, easy to maintain.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.