开发者

.NET XmlSerializer bug when two classes have properties with the same name

开发者_JAVA百科

Have your flaming torches ready people! Someone on the Internet is suggesting there is a bug in the .NET framework! :-)

Consider this contrived model:

public class Barn
{
    public Horse Horse { get; set; }
    public Motorcycle Motorcycle { get; set; }
}

public class Horse
{
    public Horse()
    {
        Rider = "Jim Craig";
    }

    [XmlElement(IsNullable = true, Namespace = "abc")]
    public string Rider { get; set; }
}

public class Motorcycle
{
    public Motorcycle()
    {
        Rider = "Ghost";
    }

    [XmlElement(IsNullable = false, Namespace = "abc")]
    public string Rider { get; set; }
}

Note the two Rider properties that share the same name and namespace, but are otherwise unrelated. One is XML nullable, the other is not.

Here is some code to XML serialize and then deserialize a null horse rider:

// Create a Barn, with a Horse, with a null Rider
Barn barnBefore = new Barn();
barnBefore.Horse = new Horse();
barnBefore.Horse.Rider = null;

// Serialize and then deserialize the Barn
XmlSerializer serializer = new XmlSerializer(typeof(Barn));
MemoryStream stream = new MemoryStream();
serializer.Serialize(stream, barnBefore);
stream.Seek(0, SeekOrigin.Begin);
Barn barnAfter = (Barn)serializer.Deserialize(stream);

// Is the Horse's Rider still null?
Console.WriteLine(barnAfter.Horse.Rider == null ? "null" : "not null");

The Horse's Rider property has IsNullable = true, so its null value should be maintained through the serialization. Running the above code, the output is indeed null. So far so good.

Now, here's the fun part: swap the order of the Horse and Motorcycle properties in the Barn class, so that Motorcycle comes first.

Run it again, and now the output is not null. What the hell?!

I've tested on VS 2008 and 2010.

For more fun, try making the two Rider properties different types, for example one a string and the other an int. That really screw ups up XmlSerializer.

I'm not interested in how to work around this issue. That's trivial. My question is: is this a bug, or what am I misunderstanding?


Well, in the first case the written XML looks a bit like this:

<Barn>
  <Horse>
    <Rider xsi:nil="true" xmlns="abc" />
  </Horse>
</Barn>

However in the second case the XML looks like this:

<Barn>
  <Horse />
</Barn>

So in the second case essentially what is happening during deserialisation is this:

Barn barn = new Barn();
barn.Hose = new Horse();

And so because we are setting the Rider by default in the constructor it is left as "Jim Craig".

What is interesting is that the behaviour can be reversed simply by changing the order of the elements in Barn. To try and discover more we can do some digging around using ILSpy and Sgen.exe - simply run sgen against our assembly and take a peek at the generated assembly.

What we find is that in the first case the generated code in both the Write2_Horse and Write3_Motorcycle methods has a line that looks like this:

base.WriteNullableStringLiteral("Rider", "abc", o.Rider);

However in the second case it looks like this:

base.WriteElementString("Rider", "abc", o.Rider);

I can't see any reason why changing the order of the elements like this should change the serialisation behaviour of both the Horse and Motorcycle elements in this this way, so in short this looks like to me like a bug in the XML serialisation assembly generator.

You should probably report it to Microsoft :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜