views:

799

answers:

4

Hello,

I am running into quite an annoying issue while trying to deserialise a specific XML document using XmlSerializer.Deserialize() method.

Basically, I have a strongly typed XSD with an element of type double. When trying to deserialise the element for a specific XML document, I get the usual "System.FormatException: Input string was not in a correct format." exception because in that specific document, the element does not have a value.

Here is some code for you nerds out there.

Sample XML document:

<TrackInfo>
  <Name>Barcelona</Name>
  <Length>4591</Length>
  <AverageSpeed />
</TrackInfo>

XSD:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
<xs:element name="TrackInfo">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Length" type="xs:double" default="0.0" />
      <xs:element name="AverageSpeed" type="xs:double" default="0.0" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

TrackInfo class:

[Serializable]
public class TrackInfo
{
  private string name = string.Empty;
  private double length = 0.0;
  private double averageSpeed = 0.0;

  [XmlElement]
  public string Name
  { ... }

  [XmlElement]
  public double Length
  { ... }

  [XmlElement]
  public double AverageSpeed
  { ... }
}

Sample deserialisation code:

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load("TrackInfo.xml");

// Deserialise XML string into TrackInfo object
byte[] buffer = ASCIIEncoding.UTF8.GetBytes(xmlDocument.InnerXml);
MemoryStream stream = new MemoryStream(buffer);
System.Xml.XmlReader reader = new System.Xml.XmlTextReader(stream);

XmlSerializer xSerializer = new System.Xml.Serialization.XmlSerializer(typeof(TrackInfo));
TrackInfo trackInfo = (TrackInfo)xSerializer.Deserialize(reader);

I know that the deserialisation exception comes from the fact that an empty string cannot be converted to a double. I also know that the default value is not assigned to AverageSpeed because, effectively, an empty string is a perfectly acceptable value.

Is there an easy way to default double values to 0.0 (or any other type) when deserialising if an empty string value is found in the XML document? Ideally, I would like to avoid implementing ISerializable because I don't really feel like spending the rest of the day into the burning pit of hell (i.e. implementing ISerializable for about a hundred classes).

Cheers! Jean-Michel

A: 

You could edit the schema to specify that the AverageSpeed value is not nullable. This requires that whatever produces the XML actually follows the schema, of course.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
<xs:element name="TrackInfo">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Length" type="xs:double" default="0.0" />
      <xs:element name="AverageSpeed" type="xs:double" default="0.0" nillable="false" />
    </xs:sequence>
  </xs:complexType>
</xs:element>
Jeff Yates
A: 

One possible solution would be to use double? (with the question mark, or Nullable<double>). This allows your value type to store a null value, and the deserializer won't choke on a null value.

Lusid
A: 

You can specify the default value like

    [XmlElement]
    [System.ComponentModel.DefaultValueAttribute(0.0)]
    public double AverageSpeed
    { ... }

/edit: ok, strange beaviour here. Whatever I set as value in the Attribute it's always the fields value:

private double averageSpeed = 2.0;

But no exception occurs.

Sebastian Sedlak
+1  A: 

Check out MSDN documentation on DefaultValueAttribute: http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx

Note: A DefaultValueAttribute will not cause a member to be automatically initialized with the attribute's value. You must set the initial value in your code.

I think the field initialisation will always have precedence over the DefaultValueAttribute value. In any case, this seems to be exactly what I was looking for.

Many thanks to you Sebastian!

jmlaplante