使用lxml,如何找到父节点的兄弟节点?

时间:2012-08-16 01:25:45

标签: python xml xpath lxml

XML一直在抛弃曲线球。我很难找到一本我能理解的手册。所以我为过去几天的所有问题道歉。

无论如何,我有以下XML:

      <clade>
        <clade>
          <branch_length>0.5</branch_length>
          <clade>
            <name>MnPV1</name>
            <annotation>
<desc>Iotapapillomavirus 1</desc></annotation><chart><group>Iota</group></chart><branch_length>1.0</branch_length>
          </clade>
          <clade> 

我想将其更改为:

  <clade>
    <clade>
      <branch_length>0.5</branch_length>
      <clade>
        <name bgstyle="green">MnPV1</name>
        <annotation><desc>Iotapapillomavirus 1</desc><uri>http://pave.niaid.nih.gov/#fetch?id=MnPV1REF&amp;format=Locus%20view&amp;hasStructure=none</uri></annotation><chart><group>Iota</group></chart><branch_length>1.0</branch_length>
      </clade>
      <clade>

所以我想改变:

<name>MnPV1</name>

为:

<name bgstyle="green">MnPV1</name>

问题是,我正在寻找是否:

tree.xpath('//phylo:group[text()="Iota"]

如果是的话我想得到“组”节点的“叔叔”,这样我就可以编辑“名字”节点

这是我到目前为止所提出的:

tree = lxml.etree.XML(data)
nsmap = {'phylo': 'http://www.phyloxml.org'}
matches = tree.xpath('//phylo:group[text()="Iota"]', namespaces=nsmap)
for e in matches:
    uncle=e.getparent().getsibling() #however, getsibling() does not exist...

我将不胜感激任何帮助(和/或lxml for dummies的建议)

2 个答案:

答案 0 :(得分:5)

这个怎么样?

>>> data = r'''<clade>
...  <name>MnPV1</name>
...  <annotation>
...    <desc>Iotapapillomavirus 1</desc>
...  </annotation>
...  <chart>
...    <group>Iota</group>
...  </chart>
...  <branch_length>1.0</branch_length>
... </clade>'''
...
>>> tree = lxml.etree.XML(data)
>>> for name in tree.xpath('//group[text()="Iota"]/../preceding-sibling::name'):
...   name.attrib['bgstyle'] = 'green'
...
>>> print lxml.etree.tostring(tree, pretty_print=True)
<clade>
 <name bgstyle="green">MnPV1</name>
 <annotation>
   <desc>Iotapapillomavirus 1</desc>
 </annotation>
 <chart>
   <group>Iota</group>
 </chart>
 <branch_length>1.0</branch_length>
</clade>

>>>

诀窍是使用XML工具(例如,XPath和XSLT)来操作XML文档。 w3schools sites是非常好的起点。 XPath本身就相当强大,一旦掌握了它就很容易阅读。但是,使用XSLT可以最好地解决此类问题。如果您要操作一堆XML,请自己做一件大事,并购买Oxygen XML editor或类似内容的副本。

如果您正在寻找使用较少XPath和更多Python的内容,请使用getparent,然后调用getprevious。我不确定支持getparentgetprevious的支持程度如何,但它们已记录在案并且可以正常工作。

答案 1 :(得分:2)

以下是一个简单的XSLT解决方案:

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

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="clade[chart/group='Iota']/name">
  <name bgstyle="green"><xsl:apply-templates/></name>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<clade>
    <clade>
        <branch_length>0.5</branch_length>
        <clade>
            <name>MnPV1</name>
            <annotation>
                <desc>Iotapapillomavirus 1</desc>
            </annotation>
            <chart>
                <group>Iota</group>
            </chart>
            <branch_length>1.0</branch_length>
        </clade>
    </clade>
</clade>

产生了想要的正确结果

<clade>
   <clade>
      <branch_length>0.5</branch_length>
      <clade>
         <name bgstyle="green">MnPV1</name>
         <annotation>
            <desc>Iotapapillomavirus 1</desc>
         </annotation>
         <chart>
            <group>Iota</group>
         </chart>
         <branch_length>1.0</branch_length>
      </clade>
   </clade>
</clade>

说明:

  1. 使用和覆盖 identity rule ,它按“原样”复制每个节点(选择执行该节点)。

  2. 匹配所需name元素的简单覆盖模板,并将其替换为具有所需新属性的同名元素。

相关问题