Ignorer les espaces de noms dans LINQ to XML

87

Comment faire pour que LINQ to XML iqnore tous les espaces de noms? Ou alternativement, comment supprimer les espaces de noms?

Je demande parce que les espaces de noms sont définis de manière semi-aléatoire et que je suis fatigué de devoir rechercher des nœuds avec et sans espace de noms.

Jonathan Allen
la source

Réponses:

137

Au lieu d'écrire:

nodes.Elements("Foo")

écrire:

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

et quand vous en avez assez, créez votre propre méthode d'extension:

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

Idem pour les attributs, si vous devez souvent gérer des attributs d'espacement de noms (ce qui est relativement rare).

[EDIT] Ajout d'une solution pour XPath

Pour XPath, au lieu d'écrire:

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

vous pouvez utiliser la local-name()fonction:

/*[local-name() = 'foo']/*[local-name() = 'bar']
Pavel Minaev
la source
Si vous savez que l'élément que vous voulez porte un nom unique, vous pouvez ignorer tous les éléments intermédiaires avec:xDoc.Root.Descendants().Where(e => e.Name.LocalName == "SomeName");
AaronLS
17

Voici une méthode pour supprimer les espaces de noms:

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

Exemple d'utilisation:

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
la source
4

Comme j'ai trouvé cette question à la recherche d'un moyen simple d'ignorer les espaces de noms sur les attributs, voici une extension pour ignorer les espaces de noms lors de l'accès à un attribut, basée sur la réponse de Pavel (pour une copie plus facile, j'ai inclus son extension):

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
la source