Ignorieren Sie Namespaces in LINQ to XML

86

Wie kann ich LINQ to XML in allen Namespaces verwenden? Oder alternativ, wie entferne ich die Namespaces?

Ich frage, weil die Namespaces halbzufällig festgelegt werden und ich es leid bin, nach Knoten mit und ohne Namespace suchen zu müssen.

Jonathan Allen
quelle

Antworten:

137

Anstatt zu schreiben:

nodes.Elements("Foo")

schreiben:

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

und wenn Sie es satt haben, machen Sie Ihre eigene Erweiterungsmethode:

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

Das Gleiche gilt für Attribute, wenn Sie häufig mit Attributen mit Namespace arbeiten müssen (was relativ selten ist).

[EDIT] Hinzufügen einer Lösung für XPath

Für XPath, anstatt zu schreiben:

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

Sie können die local-name()Funktion verwenden:

/*[local-name() = 'foo']/*[local-name() = 'bar']
Pavel Minaev
quelle
Wenn Sie wissen, dass das gewünschte Element eindeutig benannt ist, können Sie alle Zwischenelemente überspringen mit:xDoc.Root.Descendants().Where(e => e.Name.LocalName == "SomeName");
AaronLS
17

Hier ist eine Methode zum Entfernen von 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;
}

Anwendungsbeispiel:

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
quelle
4

Da ich diese Frage auf der Suche nach einer einfachen Möglichkeit gefunden habe, Namespaces für Attribute zu ignorieren, ist hier eine Erweiterung zum Ignorieren von Namespaces beim Zugriff auf ein Attribut, basierend auf Pavel's Antwort (zum einfacheren Kopieren habe ich seine Erweiterung eingefügt):

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
quelle