Ignorar namespaces em LINQ to XML

87

Como faço para ter LINQ to XML iqnore todos os namespaces? Ou, alternativamente, como remover os namespaces?

Estou perguntando porque os namespaces estão sendo definidos de forma semi-aleatória e estou cansado de ter que procurar nós com e sem um namespace.

Jonathan Allen
fonte
Consulte também Como o XPath lida com namespaces XML?
kjhughes

Respostas:

137

Em vez de escrever:

nodes.Elements("Foo")

escrever:

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

e quando você se cansar disso, faça seu próprio método de extensão:

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

O mesmo vale para atributos, se você tiver que lidar com atributos de namespace com frequência (o que é relativamente raro).

[EDIT] Adicionando solução para XPath

Para XPath, em vez de escrever:

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

você pode usar a local-name()função:

/*[local-name() = 'foo']/*[local-name() = 'bar']
Pavel Minaev
fonte
Se você sabe que o elemento que deseja tem um nome exclusivo, pode pular todos os elementos intermediários com:xDoc.Root.Descendants().Where(e => e.Name.LocalName == "SomeName");
AaronLS
17

Este é um método para remover 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;
}

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

Como encontrei esta pergunta em busca de uma maneira fácil de ignorar namespaces em atributos, aqui está uma extensão para ignorar namespaces ao acessar um atributo, com base na resposta de Pavel (para facilitar a cópia, incluí sua extensão):

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
fonte