XmlSerializer bug when "xsi:type" is used in the XML
There’s a subtle bug in the .NET Framework v1.1 that I have just uncovered. Firstly, a little background into the issue, which is related to .NET XML serialization.
Consider following XML (later assumed to be in a file “text.xml”):
<?xml version="1.0" encoding="utf-8" ?>
<Test>
<Persons xmlns=”http://tempuri.org/”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
<Person xsi:type=”Male”>
<Name>Jeff</Name>
<FavouriteBeer>Monteith</FavouriteBeer>
</Person>
<Person xsi:type=”Female”>
<Name>Jessica</Name>
<FavouriteNailpolish>Revlon</FavouriteNailpolish>
</Person>
</Persons>
</Test>
This would have been produced if you serialize the object of type Test, defined in the following code fragment:
[XmlIncludeAttribute(typeof(Male))]
[XmlIncludeAttribute(typeof(Female))]
[XmlTypeAttribute(Namespace=”http://tempuri.org/”)]
public class Person
{
public string Name;
}
[XmlTypeAttribute(Namespace=”http://tempuri.org/”)]
public class Male : Person
{
public string FavouriteBeer;
}
[XmlTypeAttribute(Namespace=”http://tempuri.org/”)]
public class Female : Person
{
public string FavouriteNailpolish;
}
[XmlTypeAttribute(Namespace=”http://tempuri.org/”)]
[XmlRootAttribute(Namespace=”http://tempuri.org/”)]
public class Test
{
public Person[] Persons;
}
The problem is that now you would not be able to deserialize the XML, if you write code similar to following:
XmlDocument doc = new XmlDocument();
doc.Load( “test.xml” );
XmlSerializer serializer = new XmlSerializer( typeof( Test ) );
Test test = (Test) serializer.Deserialize( new XmlNodeReader( doc ) );
And this is due to a bug in the XmlNodeReaderclass. I uncovered it only by stepping through the temporary serialization assembly generated by .NET framework. Without going into more detail, it is because LookupNamespace method does not return a canonical string from the reader’s NameTable. So the reference equality on the namespace inside the generated reader fails, and you get an exception similar to:
“The specified type was not recognized: name='Male', namespace='http://tempura.org/', at <Person xmlns='http://tempuri.org/'>.”Fixing the problem is quite easy. All we have to do is write a class which extends from XmlNodeReader, and override the LookupNamespace method:
public class ProperXmlNodeReader : XmlNodeReader
{
public ProperXmlNodeReader( XmlNode node ) : base( node )
{
}
public override string LookupNamespace(string prefix)
{
return NameTable.Add( base.LookupNamespace( prefix ) );
}
}
Then you can use the ProperXmlNodeReader instead of the XmlNodeReader in the above example, and you should be able to deserialize the above XML with no problems.
No comments:
Post a Comment