views:

299

answers:

4

According to W3C standards, if you have a nillable element with a nil value, you are supposed to format it like this:

<myNillableElement xsi:nil="true" />

But if you use this LinqToXml statement...

element.Add(
    new XElement(ns + "myNillableElement", null);

...the resulting XML is...

<myNillableElement />

...which is invalid. And not just invalid according to W3C, invalid according to Microsoft's own XML/XSD validator. So, next time you validate your XML, you get errors.

Am I missing some switch that can turn on correct handling of nillable elements?

Thanks.

A: 

Hopefully, this is not the ideal answer, but I wrote a couple extension methods to at least make it a little easier to deal with nillable elements in LinqToXml.

Extension Methods:

public static class XElementExtensions
{
    private static XName _nillableAttributeName = "{http://www.w3.org/2001/XMLSchema-instance}nil";

    public static void SetNillableElementValue(this XElement parentElement, XName elementName, object value)
    {
        parentElement.SetElementValue(elementName, value);
        parentElement.Element(elementName).MakeNillable();
    }

    public static XElement MakeNillable(this XElement element)
    {
        var hasNillableAttribute = element.Attribute(_nillableAttributeName) != null;
        if (string.IsNullOrEmpty(element.Value))
        {
            if (!hasNillableAttribute)
                element.Add(new XAttribute(_nillableAttributeName, true));
        }
        else
        {
            if (hasNillableAttribute)
                element.Attribute(_nillableAttributeName).Remove();
        }
        return element;
    }
}

Example Usage

// "nil" attribute will be added
element.Add(
    new XElement(NS + "myNillableElement", null)
    .MakeNillable();

// no attribute will be added
element.Add(
    new XElement(NS + "myNillableElement", "non-null string")
    .MakeNillable();

// "nil" attribute will be added (if not already present)
element.SetNillableElementValue(NS + "myNillableElement", null);

// no attribute will be added (and will be removed if necessary)
element.SetNillableElementValue(NS + "myNillableElement", "non-null string");
DanM
+3  A: 

LINQ to XML is mostly not schema-aware - it lets you validate the tree, but it doesn't derive any particular semantics from that. Your mistake is believing that null should somehow always map to xsi:nil. There's no such requirement in W3C specs (rather obviously, because they do not cover any kinds of language bindings).

In particular, XElement constructor that you call actually takes an argument of type object[], which is a list of children - there's no reason why passing null to that should have any relevance to xsi:nil. In any case, how is LINQ to XML supposed to know that you're producing XML that is valid according to some schema, and that one particular element in this schema has nilled="true"?

Pavel Minaev
Thanks for your answer...I think I'll edit the title of the post because you've convinced me that what I've observed is not really incorrect behavior. It would be nice, though, if there were a schema-aware set of classes for LinqToXml.
DanM
+2  A: 

You could also do something like this, taking advantage of the null coalescing operator:

public static object Nil
{
    get
    {
        // **I took a guess at the syntax here - you should double check.**
        return new XAttribute(Xsi + "nil", true);
    }
}

// ......

object nullableContent = ...;
element.Add(
    new XElement(NS + "myNillableElement", nullableContent ?? Nil)
    );
280Z28
Very slick! I hadn't come across `??` before. I think the extension methods are still needed to handle `SetElementValue`, though, since the attribute will either need to be added, removed, or left as is.
DanM
A: 

The extension methods are great...! Thanks

Bourn