Ignorar espacios de nombres en LINQ to XML

87

¿Cómo puedo hacer que LINQ to XML no tenga todos los espacios de nombres? O alternativamente, ¿cómo elimino los espacios de nombres?

Lo pregunto porque los espacios de nombres se establecen de forma semi-aleatoria y estoy cansado de tener que buscar nodos con y sin un espacio de nombres.

Jonathan Allen
fuente

Respuestas:

137

En lugar de escribir:

nodes.Elements("Foo")

escribir:

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

y cuando te canses, crea tu propio método de extensión:

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

Lo mismo ocurre con los atributos, si tiene que lidiar con atributos con espacios de nombres a menudo (lo cual es relativamente raro).

[EDITAR] Añadiendo solución para XPath

Para XPath, en lugar de escribir:

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

puedes usar la local-name()función:

/*[local-name() = 'foo']/*[local-name() = 'bar']
Pavel Minaev
fuente
Si sabe que el elemento que desea tiene un nombre exclusivo, puede omitir todos los elementos intermedios con:xDoc.Root.Descendants().Where(e => e.Name.LocalName == "SomeName");
AaronLS
17

Aquí hay un método para quitar espacios de nombres:

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;
}

Uso de ejemplo:

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);
Ahmad Mageed
fuente
4

Como encontré esta pregunta en la búsqueda de una manera fácil de ignorar los espacios de nombres en los atributos, aquí hay una extensión para ignorar los espacios de nombres al acceder a un atributo, basada en la respuesta de Pavel (para facilitar la copia, incluí su extensión):

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);
}
Jobo
fuente