我试图更新一个xml文档,以便将一个子元素添加到由xpath标识的一组父元素中。
为此,我标识了我希望通过xpath更改的元素,然后在这些元素上运行一个for循环。我称这段代码为“xpath for block”
我可以:
我不能:
代码:
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循环,所有更新都不会持续。
例如:
原始档案:
<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块中使用
<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块中:
<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>编辑:下面的示例最初没有插入元素。这些示例是从一个更长、更复杂的文档中简化的。我一定错过了最后的更新是在离开块后被保存的。
一旦我离开街区
<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)只有当所有的元素都在相同的深度(幸运的是,在这个例子中它们恰好是这样):
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)任何想法或意见将是非常欢迎!
发布于 2017-10-11 12:02:16
一位没有堆叠溢出的同事对此作出了有益的回应。
这个问题与this issue here有关。我认为我正在创建的given_name元素是一个空白元素,每次在循环中我都可以添加到父文档中(就像我在做字符串连接一样)。
事实上,lxml将元素视为一个不同的对象,只是在文件周围移动它(上面的例子已经简化了,所以我肯定错过了插入的最后一个位置)。
解决方案是每次使用一个小的私有函数来生成一个新元素。
修正后的代码:
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_texthttps://stackoverflow.com/questions/46687167
复制相似问题