我正在解析一些XML配置以使用其中的设置。我不打算再写出XML,所以我的兴趣只在于提取。
我有两个lxml.objectify元素:一方面是包含默认全局设置的元素,另一方面是包含特定于实例的设置。每个元素的结构类似(例如,root.global_settings.lights与root.instance_settings.lights保持相同的设置)但是可以存在交集以及设置差异。元素包含一些子文本节点,但也包含包含其他节点的节点。
我想要的:包含两个元素的所有设置的单个元素。特定于实例的设置会覆盖全局设置。
我目前拥有的最佳解决方案是循环实例子节点并覆盖/添加到全局子节点(在有文本节点的所有级别)。我想也许会有更像dict.update的东西?
编辑:举一个例子
<global>
<birthday>Unknown</birthday>
<wishes>
<cake>Chocolate</cake>
<gift>Book</gift>
</wishes>
</global>
<instance>
<name>Mike</name>
<birthday>06-06-1974</birthday>
<wishes>
<notes>Hates flowers</notes>
</wishes>
<instance>
会产生与我在
上运行objectify.parse相同的效果<global>
<name>Mike</name>
<birthday>06-06-1974</birthday>
<wishes>
<cake>Chocolate</cake>
<gift>Book</gift>
<notes>Hates flowers</notes>
</wishes>
</global>
答案 0 :(得分:0)
我没有找到任何“本地人”。 lxml解决方案。 Objectify元素具有来自两个词典的特征(您可以像dict一样访问它们的值)和列表(您可以使用其他元素扩展和追加元素)。但是,更新根本不起作用,并且扩展有严重的限制,包括缺乏递归。
所以我把这个递归函数放在一起,用另一个元素更新一个元素。此处的具体上下文是使用用户设置覆盖默认设置,在没有用户设置的情况下保留默认值。
本质上,该函数区分由两个特征定义的四种节点:
1)默认设置中是否缺少节点?如果是这样,我们可以复制(追加)用户一个。
2)如果在默认设置中也找到节点 ,我们需要进一步区分:它是一个DataElement - 即一个具有直接数据值的节点,例如: <name>Mike</name>
- 或更多结构&#39;没有直接数据值的节点,例如上例中的<wishes>...</wishes>
。在第一种情况下,我们用用户1替换默认节点(和值)。在第二种情况下,我们需要更深入一级并重复整个过程。
def merge(user_el, default_el):
'''Updating one lxml objectify elements with another'''
for child in user_el.iterchildren():
default_child = default_el.find(child.tag)
if default_child is None:
default_el.append(child)
continue
if isinstance(child, objectify.ObjectifiedDataElement):
default_el.replace(default_child, child)
elif isinstance(child, objectify.ObjectifiedElement):
merge(child, default_child)
编辑:测试上面的内容让我意识到,如果结构用户元素也存在于默认值中,例如作为空节点,具有多个具有相同标记名称的子节点,它们将逐渐替换彼此的数据子节点。为了避免这种情况,我创建了一个编辑默认设置副本的版本。这样我们就可以继续检查空的占位符元素,而不是我们逐渐填充的元素。
new_xml = copy.deepcopy(DEFAULT_XML)
merge(user_xml, new_xml, DEFAULT_XML)
def merge(user_el, new_el, default_el):
'''Updating one lxml objectify elements with another'''
for child in user_el.iterchildren():
new_child = new_el.find(child.tag)
default_child = default_el.find(child.tag)
if default_child is None:
new_el.append(child)
continue
if isinstance(child, objectify.ObjectifiedDataElement):
new_el.replace(new_child, child)
elif isinstance(child, objectify.ObjectifiedElement):
merge(child, new_child, default_child)