Pertanyaan Abaikan ruang nama di LINQ ke XML


Bagaimana saya memiliki LINQ to XML iqnore semua namespace? Atau secara terbalik, bagaimana cara menghapus ruang nama?

Saya bertanya karena ruang nama telah diatur dalam mode semi-acak dan saya lelah harus mencari node dengan dan tanpa namespace.


76
2017-07-17 20:59


asal


Jawaban:


Alih-alih menulis:

nodes.Elements("Foo")

menulis:

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

dan ketika Anda bosan, buatlah metode ekstensi Anda sendiri:

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

Ditto untuk atribut, jika Anda harus berurusan dengan atribut namespaced sering (yang relatif jarang).

[EDIT] Menambah solusi untuk XPath

Untuk XPath, alih-alih menulis:

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

Kamu dapat memakai local-name() fungsi:

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

124
2017-07-17 21:03



Berikut adalah metode untuk menghapus ruang nama:

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

Contoh penggunaan:

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



Ketika saya menemukan pertanyaan ini dalam mencari cara mudah untuk mengabaikan ruang nama pada atribut, inilah ekstensi untuk mengabaikan ruangnama ketika mengakses atribut, berdasarkan jawaban Pavel (untuk mempermudah penyalinan, saya menyertakan ekstensinya):

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