使用 python 和 lxml 访问重复的特定 xml 元素

Accessing a specific xml element where it is a duplicate using python and lxml

我有一个 xml 文件,如下所示

  <_gmd_citation>
    <_gmd_CI_Citation>
      <_gmd_title xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
          <_gco_CharacterString>Conservation Areas</_gco_CharacterString>
        </_gmd_title>
      <_gmd_alternateTitle _gco_nilReason="missing" />
      <_gmd_date>
        <_gmd_CI_Date>
          <_gmd_date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
              <_gco_Date>2018-07-24</_gco_Date>
            </_gmd_date>
          <_gmd_dateType>
            <_gmd_CI_DateTypeCode codeListValue="publication" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
          </_gmd_dateType>
        </_gmd_CI_Date>
      </_gmd_date>
      <_gmd_date>
        <_gmd_CI_Date>
          <_gmd_date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
              <_gco_Date>2013-11-15</_gco_Date>
            </_gmd_date>
          <_gmd_dateType>
            <_gmd_CI_DateTypeCode codeListValue="creation" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
          </_gmd_dateType>
        </_gmd_CI_Date>
      </_gmd_date>
      <_gmd_date>
        <_gmd_CI_Date>
          <_gmd_date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
              **<_gco_Date>2016-11-11</_gco_Date>**
            </_gmd_date>
          <_gmd_dateType>
            <_gmd_CI_DateTypeCode codeListValue="**revision**" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
          </_gmd_dateType>
        </_gmd_CI_Date>
      </_gmd_date>
      <_gmd_identifier>
        <_gmd_RS_Identifier>
          <_gmd_authority _gco_nilReason="missing" />
          <_gmd_code>
            <_gco_CharacterString>0000</_gco_CharacterString>
          </_gmd_code>
          <_gmd_codeSpace xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
              <_gco_CharacterString>abc</_gco_CharacterString>
            </_gmd_codeSpace>
        </_gmd_RS_Identifier>
      </_gmd_identifier>
    </_gmd_CI_Citation>

如果 codeListValue 为 == 'revision',我想做的是更改 _gco_Date。

我遇到的问题是元素 _gco_date 出现了多次。 我可以像这样遍历元素

for elem in treed.getiterator():
    print elem.tag
        if elem.tag == '_gmd_CI_DateTypeCode':
            if elem.attrib['codeListValue'] == 'revision':
                aa = elem.attrib['codeListValue']
                    print aa

但我似乎无法指定要更改的标签。 最好的方法是什么?

你拥有的"issue"并不是有多个_gco_Date元素,而是你的任务不是找到一个元素并对同一个元素做某事。尽管您没有明确说明这一点(并且不清楚文档可能有哪些可能的结构变化),但我可以将您的目标制定如下:

find and modify an element with tag _gco_Date that has a parent (tagged _gmd_date?) which in turn has a sibling with a child having _gmd_CI_DateTypeCode tag if and only if that latter tag has an attribute 'codeListValue' equal to 'revision'.

如果这个(或类似的东西)是您所需要的,那么您必须使用文档结构而不是简单地遍历元素而不考虑元素的位置。 element-tree 对象为您提供了实现此目标所需的一切(您可以获得父项、子项列表、兄弟列表等)。

这是一个可以用作基础的原始示例(不是世界上最好的编码,只是一个原型!):

import lxml.etree
p=lxml.etree.ETCompatXMLParser()
p.feed(open("test.xml").read())
d=p.close()

def dt_rev(e):
   """this finds if 'e' has a child node with the right tag and attribute value codeListValue == revision """
   for c in e.iterchildren():
     if c.tag == "_gmd_CI_DateTypeCode" and c.attrib['codeListValue'] == 'revision':
       return True
   return False

for e in d.getiterator():
    if e.tag == "_gco_Date":
        p = e.getparent()
        for s in p.itersiblings():
            if dt_rev(s):
                print ("found it!", e.text)
                # add code here to modify the element "e" as needed

不要遍历元素并测试标签名称和属性值,而是尝试使用 XPath

通过使用 predicate ([ ]),我们可以很容易地 select 我们需要的东西,而无需迭代。

示例...

** 根据评论中的讨论更新了命名空间。 **

XML 输入 (input.xml)

<gmd:citation xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gco="http://www.isotc211.org/2005/gco">
    <gmd:CI_Citation>
        <gmd:title xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
            <gco:CharacterString>Conservation Areas</gco:CharacterString>
        </gmd:title>
        <gmd:alternateTitle gco:nilReason="missing" />
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>2018-07-24</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="publication" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>2013-11-15</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="creation" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>2016-11-11</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="revision" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode" />
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:identifier>
            <gmd:RS_Identifier>
                <gmd:authority gco:nilReason="missing" />
                <gmd:code>
                    <gco:CharacterString>0000</gco:CharacterString>
                </gmd:code>
                <gmd:codeSpace xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:CharacterString>abc</gco:CharacterString>
                </gmd:codeSpace>
            </gmd:RS_Identifier>
        </gmd:identifier>
    </gmd:CI_Citation>
</gmd:citation>

Python

from lxml import etree

tree = etree.parse("input.xml")

ns = {"gmd": "http://www.isotc211.org/2005/gmd", "gco": "http://www.isotc211.org/2005/gco"}

try:
    elem = tree.xpath("//gmd:CI_Date[gmd:dateType/gmd:CI_DateTypeCode/"
                      "@codeListValue='revision']/gmd:date/gco:Date", namespaces=ns)[0]
    elem.text = "NEW VALUE"
except IndexError:
    pass

print etree.tostring(tree, pretty_print=True)

输出

<gmd:citation xmlns:gmd="http://www.isotc211.org/2005/gmd" xmlns:gco="http://www.isotc211.org/2005/gco">
    <gmd:CI_Citation>
        <gmd:title xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
            <gco:CharacterString>Conservation Areas</gco:CharacterString>
        </gmd:title>
        <gmd:alternateTitle gco:nilReason="missing"/>
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>2018-07-24</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="publication" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode"/>
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>2013-11-15</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="creation" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode"/>
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:date>
            <gmd:CI_Date>
                <gmd:date xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:Date>NEW VALUE</gco:Date>
                </gmd:date>
                <gmd:dateType>
                    <gmd:CI_DateTypeCode codeListValue="revision" codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/Codelist/gmxCodelists.xml#CI_DateTypeCode"/>
                </gmd:dateType>
            </gmd:CI_Date>
        </gmd:date>
        <gmd:identifier>
            <gmd:RS_Identifier>
                <gmd:authority gco:nilReason="missing"/>
                <gmd:code>
                    <gco:CharacterString>0000</gco:CharacterString>
                </gmd:code>
                <gmd:codeSpace xmlns:gml="http://www.opengis.net/gml" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
                    <gco:CharacterString>abc</gco:CharacterString>
                </gmd:codeSpace>
            </gmd:RS_Identifier>
        </gmd:identifier>
    </gmd:CI_Citation>
</gmd:citation>

重要提示:确保两个名称空间 URI(http://www.isotc211.org/2005/gmdhttp://www.isotc211.org/2005/gco)与您在 xml 中的完全匹配。您评论中的 URI 已自动设置格式,因此未显示 "http://" 部分。

此外,see here 了解有关在 lxml 中使用带有命名空间的 XPath 的更多信息。