8

I wanted to deserialize an XML message containing an element that can be marked nil="true" into a class with a property of type int?. The only way I could get it to work was to write my own NullableInt type which implements IXmlSerializable. Is there a better way to do it?

I wrote up the full problem and the way I solved it on my blog.

James A Mohler
  • 11,060
  • 15
  • 46
  • 72
Alex Scordellis
  • 492
  • 5
  • 16

3 Answers3

6

I think you need to prefix the nil="true" with a namespace in order for XmlSerializer to deserialise to null.

MSDN on xsi:nil

<?xml version="1.0" encoding="UTF-8"?>
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="array">
  <entity>
    <id xsi:type="integer">1</id>
    <name>Foo</name>
    <parent-id xsi:type="integer" xsi:nil="true"/>
Phil Jenkins
  • 4,396
  • 1
  • 27
  • 22
  • Thanks Phil, that worked a treat. Now I just need to get the guy providing the messages to add the namespace... – Alex Scordellis Nov 21 '08 at 09:11
  • No worries SCOA :-P If you can't change the XML at source (sounds like you can though) you could pass it through an XSLT transform to add the namespace I think... – Phil Jenkins Nov 21 '08 at 09:16
  • Yes JENP, turns out it's tricky to do it at source as it's a Rails app producing the xml with `to_xml`. Also, adding xsi: to the type attribute makes .NET look for a type called `array`, which doesn't exist. So in the end it's going to be easier to stick with my `NullableInt` type. – Alex Scordellis Nov 21 '08 at 09:49
  • I have the exact same issue, can't get the api changed and am probably going to go the XSLT route (although I'm not getting very far with it) – ChadT Jun 22 '10 at 05:49
3

My fix is to pre-process the nodes, fixing any "nil" attributes:

public static void FixNilAttributeName(this XmlNode @this)
{
    XmlAttribute nilAttribute = @this.Attributes["nil"];
    if (nilAttribute == null)
    {
        return;
    }

    XmlAttribute newNil = @this.OwnerDocument.CreateAttribute("xsi", "nil", "http://www.w3.org/2001/XMLSchema-instance");
    newNil.Value = nilAttribute.Value;
    @this.Attributes.Remove(nilAttribute);
    @this.Attributes.Append(newNil);
}

I couple this with a recursive search for child nodes, so that for any given XmlNode (or XmlDocument), I can issue a single call before deserialization. If you want to keep the original in-memory structure unmodified, work with a Clone() of the XmlNode.

Tom Mayfield
  • 6,235
  • 2
  • 32
  • 43
  • That worked very well for me, thanks. And explicitly for Rails consumption. I did make a slight adjustment to check for non-null attributes:XmlAttribute nilAttribute = null; if (@this.Attributes != null) { nilAttribute = @this.Attributes["nil"]; } – Dylan Feb 20 '11 at 01:11
0

The exceptionally lazy way to do it. It's fragile for a number of reasons but my XML is simple enough to warrant such a quick and dirty fix.

xmlStr = Regex.Replace(xmlStr, "nil=\"true\"", "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"");
sipsorcery
  • 30,273
  • 24
  • 104
  • 155