首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >lxml xpath/findall和更新

lxml xpath/findall和更新
EN

Stack Overflow用户
提问于 2017-10-11 11:31:33
回答 1查看 1.4K关注 0票数 0

我试图更新一个xml文档,以便将一个子元素添加到由xpath标识的一组父元素中。

为此,我标识了我希望通过xpath更改的元素,然后在这些元素上运行一个for循环。我称这段代码为“xpath for block”

我可以:

  • 成功地确定需要更新的元素
  • 在“xpath for块”中更新元素
  • 在“xpath for块”中更新根节点

我不能:

  • 保留更新一次,留下'xpath‘(编辑,除了循环中的最后一个元素)

代码:

代码语言:javascript
复制
from lxml import etree as ET

def _modify_mods_content(mods_content):
    """Massage the MODS content before auto-populating page

    Args:
        mods_content (str): content of MODS datastream as a string

    Returns:
        updated_content (str): updated MODS content
    """
    nsmap = {'mods': NAMESPACES['mods']}

    mods_xml = ET.fromstring(mods_content)

    # Create empty given-name element
    given_name_tag = ET.QName(NAMESPACES['mods'], 'namePart')
    given_name = ET.Element(given_name_tag)
    given_name.attrib['type'] = 'given'


    # xpath to find every personal name that does not have a child 
    # given name element
    no_given_names_xpath = '//mods:name[@type="personal"][not(mods:namePart[@type="given"])]'


    for element in mods_xml.xpath(no_given_names_xpath, namespaces=nsmap):
        element.insert(0, given_name)

    new_string = ET.tostring(mods_xml)
    return new_string

如果我将一个pdb提示符放入xpath块中,我可以看到元素正在更新,如果我一直在树上执行一个“getparent”操作,我可以看到根mods_xml文档似乎只对该实例进行了更新。

一旦我离开for循环,所有更新都不会持续。

例如:

原始档案:

代码语言:javascript
复制
<root>
  <mods:name type="personal">
    <mods:namePart type="family">Smith</mods:namePart>
  </mods:name>
  <mods:name type="personal">
    <mods:namePart type="family">Jones</mods:namePart>
  </mods:name>
</root>

第一次在xpath块中使用

代码语言:javascript
复制
<root>
  <mods:name type="personal">
    <mods:namePart type="given"></mods:namePart>
    <mods:namePart type="family">Smith</mods:namePart>
  </mods:name>
  <mods:name type="personal">
    <mods:namePart type="family">Jones</mods:namePart>
  </mods:name>
</root>

第二次在xpath for块中:

代码语言:javascript
复制
<root>
  <mods:name type="personal">
    <mods:namePart type="family">Smith</mods:namePart>
  </mods:name>
  <mods:name type="personal">
    <mods:namePart type="given"></mods:namePart>
    <mods:namePart type="family">Jones</mods:namePart>
  </mods:name>
</root>

编辑:下面的示例最初没有插入元素。这些示例是从一个更长、更复杂的文档中简化的。我一定错过了最后的更新是在离开块后被保存的。

一旦我离开街区

代码语言:javascript
复制
<root>
  <mods:name type="personal">
    <mods:namePart type="family">Smith</mods:namePart>
  </mods:name>
  <mods:name type="personal">
    <mods:namePart type="given"></mods:namePart>
    <mods:namePart type="family">Jones</mods:namePart>
  </mods:name>
</root>

我意识到这可以用XSLT来完成。我只想知道是否还有更多的“琵琶”的方法。

实际上,我已经(某种程度上)让它工作了,但这是一个非常可怕的攻击,a)我不想这样做,b)只有当所有的元素都在相同的深度(幸运的是,在这个例子中它们恰好是这样):

代码语言:javascript
复制
    while mods_xml.xpath(no_given_names_xpath, namespaces=nsmap):
        elements = mods_xml.xpath(no_given_names_xpath, namespaces=nsmap)
        replacement_element = elements[0]
        replacement_element.insert(0, given_name)
        parent = elements[0].getparent()
        parent.replace(elements[0], replacement_element)
        new_xml_string = ET.tostring(parent, pretty_print=True)
        mods_xml = ET.fromstring(new_xml_string)

任何想法或意见将是非常欢迎!

EN

回答 1

Stack Overflow用户

发布于 2017-10-11 12:02:16

一位没有堆叠溢出的同事对此作出了有益的回应。

这个问题与this issue here有关。我认为我正在创建的given_name元素是一个空白元素,每次在循环中我都可以添加到父文档中(就像我在做字符串连接一样)。

事实上,lxml将元素视为一个不同的对象,只是在文件周围移动它(上面的例子已经简化了,所以我肯定错过了插入的最后一个位置)。

解决方案是每次使用一个小的私有函数来生成一个新元素。

修正后的代码:

代码语言:javascript
复制
def _modify_mods_content(mods_content):
    """Massage the MODS content before auto-populating xml_ora

    Args:
        mods_content (str): content of MODS datastream as a string

    Returns:
        updated_content (str): updated MODS content
    """
    nsmap = {'mods': NAMESPACES['mods']}
    try:
        mods_xml = ET.fromstring(mods_content)

        def __create_empty_given_name_element():
            """Create empty given-name element

            Returns:
                given_name (ET.element): empty element for processing
            """
            given_name_tag = ET.QName(NAMESPACES['mods'], 'namePart')
            given_name = ET.Element(given_name_tag)
            given_name.attrib['type'] = 'given'
            given_name.text = ''
            given_name.tail = '\n      '
            return given_name

        # xpath to find every personal name that does not have a child given name element
        no_given_names_xpath = '//mods:name[@type="personal"][not(mods:namePart[@type="given"])]'

        for element in mods_xml.xpath(no_given_names_xpath, namespaces=nsmap):
            given_name_element = __create_empty_given_name_element()
            element.insert(0, given_name_element)

        return_text = ET.tostring(mods_xml)   
    except Exception as _:  # pylint: disable=W0703
        # Something went wrong, but try to let a human fix it
        # by returning original MODS
        return mods_content
    return return_text
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46687167

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档