Pregunta Ignorar espacios de nombres en LINQ a XML


¿Cómo tengo LINQ to XML iqnore todos los espacios de nombres? O, como alternativa, ¿cómo puedo eliminar los espacios de nombres?

Pregunto porque los espacios de nombres se configuran de forma semialeatoria y estoy cansado de tener que buscar nodos con y sin un espacio de nombres.


76
2017-07-17 20:59


origen


Respuestas:


En lugar de escribir:

nodes.Elements("Foo")

escribir:

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

y cuando te canses de eso, 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 para los atributos, si tiene que lidiar con atributos de espacio de nombres a menudo (lo cual es relativamente raro).

[EDIT] 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 local-name() función:

/*[local-name() = 'foo']/*[local-name() = 'bar']

124
2017-07-17 21:03



Aquí hay un método para eliminar 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;
}

Ejemplo de uso:

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

15
2017-07-18 08:24



Cuando encontré esta pregunta en busca 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, basado 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);
}

4
2018-01-29 16:01