Нежелательное объявление пространства имен в lxml XPath - PullRequest
6 голосов
/ 29 февраля 2012

Я хочу выбрать первый дочерний элемент определенного элемента (subelement), но пространство имен этого дочернего элемента отличается от пространства имен родителя.Более того, этот ребенок может быть из любого пространства имен.

xml = '''<root xmlns="default_ns">
    <subelement>
        <!-- here we can have an element of any namespace  -->
        <some_prefix:a xmlns:some_prefix="some_namespace">
            <some_prefix:b/>
        </some_prefix:a>
    </subelement>
</root>'''
root = etree.fromstring(xml)
evaluator = etree.XPathEvaluator(root, namespaces={'def':'default_ns'})
child = evaluator.evaluate('//def:subelement/child::*')[0]
a_string = etree.tostring(child)
print a_string

Это дает:

<some_prefix:a xmlns:some_prefix="some_namespace" xmlns="default_ns">
    <some_prefix:b/>
</some_prefix:a>

, но я хочу получить дочерний элемент без объявления пространства имен от родителя xmlns="default_ns":

<some_prefix:a xmlns:some_prefix="some_namespace">
    <some_prefix:b/>
</some_prefix:a>

Ответы [ 2 ]

1 голос
/ 29 февраля 2012

но я хочу получить дочерний элемент без объявления пространства имен от parent xmlns = "default_ns".

Этого невозможно достичь только путем вычисления выражения XPath.

В XML любой элемент наследует все узлы пространства имен своего родителя , если только он не переопределяет определенное пространство имен.

Это означает, что some_prefix:a наследуетпространство имен по умолчанию "default_ns" от его родителя (subelement), которое само наследует этот же узел пространства имен по умолчанию от верхнего элемента root.

XPath - это язык запросов для документов XML.Таким образом, он помогает только выбирать узлы, но оценка выражения XPath никогда не разрушает, не добавляет и не изменяет узлы, включая узлы пространства имен .

. Из-за этого узел пространства имен по умолчанию, который принадлежитдо some_prefix:a не может быть уничтожено в результате оценки вашего выражения XPath - таким образом, этот узел пространства имен отображается, когда some_prefix:a сериализуется в текст.

Решение : используйте ваш любимыйPL, на котором размещается XPath, для удаления нежелательного узла пространства имен.

Например, если языком хостинга является XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:d="default_ns">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:apply-templates mode="delNS"
    select="/*/d:subelement/*[1]"/>
 </xsl:template>

 <xsl:template match="*" mode="delNS">
   <xsl:element name="{name()}" namespace="{namespace-uri()}">
    <xsl:copy-of select="namespace::*[name()]"/>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="delNS" select="node()"/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование являетсяприменяется к предоставленному документу XML :

<root xmlns="default_ns">
    <subelement>
        <!-- here we can have an element of any namespace  -->
        <some_prefix:a xmlns:some_prefix="some_namespace">
            <some_prefix:b/>
        </some_prefix:a>
    </subelement>
</root>

желаемый, правильный результат получается :

<some_prefix:a xmlns:some_prefix="some_namespace">
   <some_prefix:b/>
</some_prefix:a>
0 голосов
/ 23 октября 2013

Димитр полностью объяснил, почему пространства имен наследуются и как избавиться от него с помощью XSLT.

Я использовал deepcopy из copy для удаления нежелательного пространства имен.

Это мое окончательное решение с использованием Python:

from lxml import etree
from copy import deepcopy

xml = '''<root xmlns="default_ns">
    <subelement>
        <!-- here we can have an element of any namespace  -->
        <some_prefix:a xmlns:some_prefix="some_namespace">
            <some_prefix:b/>
        </some_prefix:a>
    </subelement>
</root>'''
root = etree.fromstring(xml)
evaluator = etree.XPathEvaluator(root, namespaces={'def':'default_ns'})
child = evaluator.evaluate('//def:subelement/child::*')[0]
child = deepcopy(child)
a_string = etree.tostring(child)
print a_string
...