2017-04-27 72 views
0

我解析了一些XML配置以使用其中的設置。我不打算再寫出XML,所以我的興趣只在於提取。如何合併兩個lxml.objectify元素?

我有兩個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> 

將產生相同的,如果我在

<global> 
    <name>Mike</name> 
    <birthday>06-06-1974</birthday> 
    <wishes> 
     <cake>Chocolate</cake> 
     <gift>Book</gift> 
     <notes>Hates flowers</notes> 
    </wishes> 
</global> 

回答

0

我沒有找到任何「天然」 LXML解決方案運行objectify.parse。 Objectify元素具有來自兩個字典的特徵(您可以像字典一樣訪問它們的值)和列表(您可以使用其他元素擴展和追加元素)。然而,更新根本不起作用,擴展有嚴重的侷限性,包括缺乏遞歸性。

所以我把這個遞歸函數放在一起,用另一個元素更新一個元素。這裏的具體上下文是使用用戶設置來覆蓋默認設置,在沒有用戶設置的情況下保留默認設置。

在本質功能4種由兩個特徵定義的節點之間進行區分:

1)是對節點不存在於默認設置?如果是這樣,我們可以複製(追加)用戶。 2)如果節點也在默認設置中找到,我們需要進一步區分:它是否是DataElement - 即具有直接數據值的節點,例如, <name>Mike</name> - 或更多不具有直接數據值的「結構」節點,例如上例中的<wishes>...</wishes>。在第一種情況下,我們用用戶替換默認節點(和值)。第二,我們需要深入一層,重複整個過程。

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) 

EDIT:測試上述使我意識到,如果一個結構用戶單元,在默認值也存在,例如作爲一個空的節點,有多個具有相同標籤名稱的子節點,它們將逐漸取代彼此的數據子節點。爲了避免這種情況,我創建了一個編輯默認設置副本的版本。這樣,我們繼續檢查空的佔位符元素,而不是我們逐漸填充的元素。

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)