Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignore namespaces in LINQ to XML

Tags:

.net

xml

linq

How do I have LINQ to XML iqnore all namespaces? Or alteranately, how to I strip out the namespaces?

I'm asking because the namespaces are being set in a semi-random fashion and I'm tired of having to search for nodes both with and without a namespace.

like image 495
Jonathan Allen Avatar asked Jul 17 '09 20:07

Jonathan Allen


People also ask

How do I change a namespace in XML?

Since in this case you have a default namespace defined, you could just remove the default namespace declaration and add a new declaration for your new prefix using the old namespace name, effectively replacing it.

How do I add namespace prefix to XML element?

To create an attribute that declares a namespace with a prefix, you create an attribute where the namespace of the name of the attribute is Xmlns, and the name of the attribute is the namespace prefix. The value of the attribute is the URI of the namespace.


3 Answers

Instead of writing:

nodes.Elements("Foo")

write:

nodes.Elements().Where(e => e.Name.LocalName == "Foo")

and when you get tired of it, make your own extension method:

public static IEnumerable<XElement> ElementsAnyNS<T>(this IEnumerable<T> source, string localName)
    where T : XContainer
{
    return source.Elements().Where(e => e.Name.LocalName == localName);
}

Ditto for attributes, if you have to deal with namespaced attributes often (which is relatively rare).

[EDIT] Adding solution for XPath

For XPath, instead of writing:

/foo/bar | /foo/ns:bar | /ns:foo/bar | /ns:foo/ns:bar

you can use local-name() function:

/*[local-name() = 'foo']/*[local-name() = 'bar']
like image 183
Pavel Minaev Avatar answered Sep 28 '22 11:09

Pavel Minaev


Here's a method to strip namespaces:

private static XElement StripNamespaces(XElement rootElement)
{
    foreach (var element in rootElement.DescendantsAndSelf())
    {
        // update element name if a namespace is available
        if (element.Name.Namespace != XNamespace.None)
        {
            element.Name = XNamespace.None.GetName(element.Name.LocalName);
        }

        // check if the element contains attributes with defined namespaces (ignore xml and empty namespaces)
        bool hasDefinedNamespaces = element.Attributes().Any(attribute => attribute.IsNamespaceDeclaration ||
                (attribute.Name.Namespace != XNamespace.None && attribute.Name.Namespace != XNamespace.Xml));

        if (hasDefinedNamespaces)
        {
            // ignore attributes with a namespace declaration
            // strip namespace from attributes with defined namespaces, ignore xml / empty namespaces
            // xml namespace is ignored to retain the space preserve attribute
            var attributes = element.Attributes()
                                    .Where(attribute => !attribute.IsNamespaceDeclaration)
                                    .Select(attribute =>
                                        (attribute.Name.Namespace != XNamespace.None && attribute.Name.Namespace != XNamespace.Xml) ?
                                            new XAttribute(XNamespace.None.GetName(attribute.Name.LocalName), attribute.Value) :
                                            attribute
                                    );

            // replace with attributes result
            element.ReplaceAttributes(attributes);
        }
    }
    return rootElement;
}

Example usage:

XNamespace ns = "http://schemas.domain.com/orders";
XElement xml =
    new XElement(ns + "order",
        new XElement(ns + "customer", "Foo", new XAttribute("hello", "world")),
        new XElement("purchases",
            new XElement(ns + "purchase", "Unicycle", new XAttribute("price", "100.00")),
            new XElement("purchase", "Bicycle"),
            new XElement(ns + "purchase", "Tricycle",
                new XAttribute("price", "300.00"),
                new XAttribute(XNamespace.Xml.GetName("space"), "preserve")
            )
        )
    );

Console.WriteLine(xml.Element("customer") == null);
Console.WriteLine(xml);
StripNamespaces(xml);
Console.WriteLine(xml);
Console.WriteLine(xml.Element("customer").Attribute("hello").Value);
like image 21
Ahmad Mageed Avatar answered Sep 28 '22 10:09

Ahmad Mageed


As I found this question in search for an easy way to ignore namespaces on attributes, here's an extension for ignoring namespaces when accessing an attribute, based on Pavel´s answer (for easier copying, I included his extension):

public static XAttribute AttributeAnyNS<T>(this T source, string localName)
where T : XElement
{
    return source.Attributes().SingleOrDefault(e => e.Name.LocalName == localName);
}

public static IEnumerable<XElement> ElementsAnyNS<T>(this IEnumerable<T> source, string localName)
where T : XContainer
{
    return source.Elements().Where(e => e.Name.LocalName == localName);
}
like image 33
Jobo Avatar answered Sep 28 '22 10:09

Jobo