Frage Ignoriere Namespaces in LINQ to XML


Wie kann ich LINQ to XML mit allen Namespaces verknüpfen? Oder alternativ, wie entferne ich die Namespaces?

Ich frage, weil die Namespaces in einer halb-zufälligen Weise gesetzt werden und ich es leid bin, nach Knoten mit und ohne Namespace suchen zu müssen.


76
2017-07-17 20:59


Ursprung


Antworten:


Anstatt zu schreiben:

nodes.Elements("Foo")

schreiben:

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

und wenn du es satt hast, mach deine 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);
}

Dito für Attribute, wenn Sie oft mit Namespaced-Attributen zu tun haben (was relativ selten ist).

[EDIT] Hinzufügen der 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 verwenden local-name() Funktion:

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

124
2017-07-17 21:03



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

Beispielverwendung:

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



Als ich diese Frage auf der Suche nach einer einfachen Möglichkeit fand, Namespaces auf Attributen zu ignorieren, gibt es hier eine Erweiterung zum Ignorieren von Namespaces beim Zugriff auf ein Attribut, basierend auf Pavels Antwort (zum leichteren Kopieren habe ich seine Erweiterung hinzugefü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);
}

4
2018-01-29 16:01