如何合并两个lxml.objectify元素?

时间:2017-04-27 07:23:41

标签: python xml xml-parsing lxml

我正在解析一些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>

1 个答案:

答案 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)
相关问题