IXmlSerializable implementation ruins xml serialization

I have a class containing a readonly string. Thus I need to manually handle xmlserialization. But it seems that using this class in a grander setting with automatic xml serialization ruins the deserialization process.

First my readonly class

public class RO : IXmlSerializable
{
  public string Name { get; private set; }

  // for serialization
  protected RO(){}
  public RO(string name)
  {
    this.Name = name;
  }

  public XmlSchema GetSchema()
  {
    return null;
  }

  public void ReadXml(XmlReader reader)
  {
    Name = reader.ReadString();
  }

  public void WriteXml(XmlWriter writer)
  {
    writer.WriteString(Name);
  }
}

then a wrapper class holding my readonly object and some value

public class Holder
{
  public RO a;
  public decimal b;
}

and now I serialize and deserialize an instance of the holder

class Program
{
  static void Main(string[] args)
  {
    XmlSerializer xml = new XmlSerializer(typeof(Holder));
    var s = new StringWriter();
    var holder = new Holder() { a = new RO("foo"), b = 234 };
    xml.Serialize(s, holder);
    string ss = s.ToString();
    Console.WriteLine("*****");
    Console.WriteLine(ss);
    Console.WriteLine("*****");
    holder = (Holder) xml.Deserialize(new StringReader(ss));
    Console.WriteLine(holder.a.Name);
    Console.WriteLine(holder.b);
  }
}

on the screen is a nice-looking xml

*****
<?xml version="1.0" encoding="utf-16"?>
<Holder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
 www.w3.org/2001/XMLSchema">
  <a>foo</a>
  <b>234</b>
</Holder>
*****

but when we print the values a and b we get 0 for our decimal.. despite we see the 234 in the xml

foo
0
Jon Skeet
people
quotationmark

I suspect the problem is that you're not reading to the end of the containing element - I suspect you want:

public void ReadXml(XmlReader reader)
{
  Name = reader.ReadElementContentAsString();
}

The documentation for ReadXml states:

When this method is called, the reader is positioned on the start tag that wraps the information for your type. That is, directly on the start tag that indicates the beginning of a serialized object. When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.

ReadString will just read a string and stop when it gets to the end element tag; ReadElementContentAsString will position the reader after the end element tag.

I've just tried the above code with your sample, and it solves the problem there, at least.

people

See more on this question at Stackoverflow