XmlSerializer issue? Mixing classes that do and don't implement IXmlSerializable

Hello,

I'm experiencing an issue with XmlSerializer. Please test the commented short C# code below.

I'm quite sure it worked before, although it had been a month or two ago. I'm not aware of upgrading the .NET Framework 2.0 since.

The problem occurs whenever I'm deserializing a class that doesn't implement IXmlSerializable but contains instance of a class that does. In such case the member holding such instance is the last one that is properly deserialized, after that the XmlSerializer silently ignores the rest of the xml file and finishes deserialization.

I wonder whether the error comes from my side, but as I stated, I was succefully using this approach before.

Here the program:

using System;

using System. Xml;

using System. Xml. Serialization;

using System. IO;

using System. Globalization;

using System. Windows. Forms;

namespace Tester

{

public class Program

{

public class MyClass

{

public MyClass()

{

}

public string SomeName;

public CustomNumber NumberX;

public CustomNumber NumberY;

public string SomeName2;

}

public class CustomNumber : IXmlSerializable

{

public CustomNumber()

{

}

public CustomNumber( float value )

{

SomeNumber = value;

}

public float SomeNumber;

#region IXmlSerializable Members

public System. Xml. Schema. XmlSchema GetSchema()

{

return null;

}

public void ReadXml( XmlReader reader )

{

SomeNumber = Convert. ToSingle( reader. ReadString(), CultureInfo. InvariantCulture );

}

public void WriteXml( XmlWriter writer )

{

writer. WriteString( Convert. ToString( SomeNumber, CultureInfo. InvariantCulture ) );

}

#endregion

}

[STAThread]

static void Main()

{

//initialize some data first

MyClass myClass = new MyClass();

myClass. SomeName = "MyClass Name";

myClass. NumberX = new CustomNumber( 3.14f );

myClass. NumberY = new CustomNumber( 123.654f );

myClass. SomeName2 = "Was deserialized ";

FileStream stream;

string fileName1 = @"C:\XmlSerializerTest_FirstStep.xml";

string fileName2 = @"C:\XmlSerializerTest_SecondStep.xml";

//serialize instance of MyClass

stream = new FileStream( fileName1 , FileMode. Create );

( new XmlSerializer( typeof( MyClass ) ) ). Serialize( stream, myClass );

stream. Close();

//later you'll see that this serialization was successfull

//deserialize from same file to a new instance of MyClass

stream = new FileStream( fileName1, FileMode. Open );

MyClass myClassDeserialized = (MyClass)( new XmlSerializer( typeof( MyClass ) ) ). Deserialize( stream );

stream. Close();

//If you'd debug this part, you'd see that the deserialization was working until CustomNumber.ReadXml() was called first.

//After that the XmlSerializer properly sets the NumberX field yet, however silently ignores the rest of the file

//and finishes serialization without warning.

//So if myClassDeserialized would be checked here you'd see that all fields after NumberX remain null.

//Let's check that and serialize the new instance again.

//serialize again to the second xml file to see the results

stream = new FileStream( fileName2, FileMode. Create );

( new XmlSerializer( typeof( MyClass ) ) ). Serialize( stream, myClassDeserialized );

stream. Close();

//(As we checked before, serialization works properly...)

MessageBox. Show( "OK" );

//Now go and check the outputs, should be equal, shouldn't they

//But the fields that were ignored by the deserialization process remain null

//and as such don't appear in the second xml file at all.

}

}

}

File: "C:\XmlSerializerTest_FirstStep.xml"

< xml version="1.0" >
<MyClass xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeName>MyClass Name</SomeName>
<NumberX>3.14</NumberX>
<NumberY>123.654</NumberY>
<SomeName2>Was deserialized </SomeName2>
</MyClass>

File: "C:\XmlSerializerTest_SecondStep.xml"

< xml version="1.0" >
<MyClass xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SomeName>MyClass Name</SomeName>
<NumberX>3.14</NumberX>
</MyClass>

Not to forget mentioning, that I need to solve it. :-) The real code where I'm using this is much complicated and contains a lot of classes, only one of them needs to implement IXmlSerializable. I definitely do not wish to implement IXmlSerializable on all of my classes if that would be your suggestion, but probably cannot omit IXmlSerializable at all! So where is the error

Thanks in advance.



Answer this question

XmlSerializer issue? Mixing classes that do and don't implement IXmlSerializable

  • angelina

    The problem is with IXmlSerializable.ReadXml() implementation: it does not consumes all relevant (CustomNumber) instance data: it need to consume the closing tag of the <NumberX>3.14</NumberX>.

    Change the IXmlSerializable.ReadXml() implementation to use XmlReader.ReadElementString() to consume the closing tag:

    public void ReadXml(XmlReader reader)

    {

    SomeNumber = Convert.ToSingle(reader.ReadElementString(), CultureInfo.InvariantCulture);

    }

    This is an unfortunate legacy behavior that could not be changed without breaking the existent code.

    Thanks,

    Elena Kharitidi


  • akaRickShaw

    Thank you, that worked.

    I just wonder whether the XmlSerializer won't report an error if it cannot consume the whole xml file, that could save some time exploring what's going on...

    however.


  • XmlSerializer issue? Mixing classes that do and don't implement IXmlSerializable