通过按属性值匹配元素来合并两个XML文件

时间:2014-11-06 02:41:44

标签: python xml python-2.7 lxml elementtree

我有两个我试图合并的XML文件。我看了之前的其他问题,但我不觉得我可以通过阅读这些问题来解决我的问题。我认为我的情况独特的是我必须按属性值查找元素然后合并到相反的文件。

我有两个文件。一个是英文翻译目录,第二个是日文翻译目录。请见下文。

在下面的代码中,您将看到XML有三个元素,我将合并子元素 - MessageCatalogueEntry,MessageCatalogueFormEntry和MessageCatalogueFormItemEntry。我有数百个文件,每个文件有数千行。可能会有比我刚刚列出的三个元素更多的元素,但我确信所有元素都有一个"键"属性。

我的计划:

  • 遍历文件1并创建"键"的所有值的列表。属性。
    • 在此示例中,列表为key_values = [321, 260, 320]
  • 接下来,我将逐个浏览key_value列表。
  • 我会在文件1中搜索属性为key=321的元素。
  • 接下来,从文件1中抓取key=321元素的子元素。
  • 接下来,在文件2中,找到带有key=321的元素,并添加我之前从文件1中抓取的子元素。
  • 接下来,我将继续循环浏览key_values列表。
  • 接下来,我将新的xml根写入文件,小心保持utf8编码。

文件1:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE MessageCatalogue []>
<PackageEntry>
    <MessageCatalogue designNotes="Undefined" isPrivate="false" lastKey="362" name="AddKMRichSearchEngineAdmin_AutoTranslationCatalogue" nested="false" version="3.12.0">
      <MessageCatalogueEntry key="321">
        <MessageCatalogueEntry_loc locale="" message="active"/>
      </MessageCatalogueEntry>
      <MessageCatalogueFormEntry key="260">
        <MessageCatalogueFormEntry_loc locale="" shortTitle="Configuration" title="Spider Configuration"/>
      </MessageCatalogueFormEntry>
      <MessageCatalogueFormItemEntry key="320">
        <MessageCatalogueFormItemEntry_loc hintText="" label="Manage Recognised Phrases" locale="" mnemonic="" scriptText=""/>
      </MessageCatalogueFormItemEntry>
    </MessageCatalogue>
  </PackageEntry>

文件2:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE MessageCatalogue[]>
<PackageEntry>
  <MessageCatalogue designNotes="Undefined" isPrivate="false" lastKey="362" name="" nested="false" version="3.12.0">
    <MessageCatalogueEntry key="321">
      <MessageCatalogueEntry_loc locale="ja" message="アクティブ" />
    </MessageCatalogueEntry>
    <MessageCatalogueFormEntry key="260">
      <MessageCatalogueFormEntry_loc locale="ja" shortTitle="設定" title="Spider Configuration/スパイダー設定" />
    </MessageCatalogueFormEntry>
    <MessageCatalogueFormItemEntry key="320">
      <MessageCatalogueFormItemEntry_loc hintText="" label="認識されたフレーズを管理" locale="ja" mnemonic="" scriptText="" />
    </MessageCatalogueFormItemEntry>
  </MessageCatalogue>
</PackageEntry>

输出:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE MessageCatalogue []>
<PackageEntry>
    <MessageCatalogue designNotes="Undefined" isPrivate="false" lastKey="362" name="AddKMRichSearchEngineAdmin_AutoTranslationCatalogue" nested="false" version="3.12.0">
      <MessageCatalogueEntry key="321">
        <MessageCatalogueEntry_loc locale="" message="active"/>
        <MessageCatalogueEntry_loc locale="ja" message="アクティブ" />
      </MessageCatalogueEntry>
      <MessageCatalogueFormEntry key="260">
        <MessageCatalogueFormEntry_loc locale="" shortTitle="Configuration" title="Spider Configuration"/>
        <MessageCatalogueFormEntry_loc locale="ja" shortTitle="設定" title="Spider Configuration/スパイダー設定" />
      </MessageCatalogueFormEntry>
      <MessageCatalogueFormItemEntry key="320">
        <MessageCatalogueFormItemEntry_loc hintText="" label="Manage Recognised Phrases" locale="" mnemonic="" scriptText=""/>
        <MessageCatalogueFormItemEntry_loc hintText="" label="認識されたフレーズを管理" locale="ja" mnemonic="" scriptText="" />
      </MessageCatalogueFormItemEntry>
    </MessageCatalogue>
  </PackageEntry>

我甚至在抓住元素方面遇到了麻烦,没有用钥匙来抓住它们。例如,我一直在使用elementtree库,我编写了这段代码,希望只获得MessageCatalogueEntry,但我只是让他们的孩子:

from xml.etree import ElementTree as et

tree_japanese = et.parse('C:\\blah\\blah\\blah\\AddKMRichSearchEngineAdmin_AutoTranslationCatalogue_JA.xml')
root_japanese = tree_japanese.getroot()
MC_japanese =  root_japanese.findall("MessageCatalogue")

for x in MC_japanese:
    messageCatalogueEntry = x.findall("MessageCatalogueEntry")
    for m in messageCatalogueEntry:
        print et.tostring(m[0], encoding='utf8')

tree_english = et.parse('C:\\blah\\blah\\blah\\AddKMRichSearchEngineAdmin\\AddKMRichSearchEngineAdmin_AutoTranslationCatalogue.xml')
root_english = tree_english.getroot()
MC_english =  root_english.findall("MessageCatalogue")

for x in MC_english:
    messageCatalogueEntry = x.findall("MessageCatalogueEntry")
    for m in messageCatalogueEntry:
        print et.tostring(m[0], encoding='utf8')

任何帮助将不胜感激。我现在已经工作了几个工作日了,而且我开始时的工作时间比我刚开始的时候还要接近完毕!

1 个答案:

答案 0 :(得分:3)

实际上,您正在获取MessageCatalogEntry。问题出在print语句中。元素的行为类似于列表,因此m[0]是MessageCatalogEntry的第一个子元素。在

messageCatalogueEntry = x.findall("MessageCatalogueEntry")
for m in messageCatalogueEntry:
    print et.tostring(m[0], encoding='utf8')

将打印更改为print et.tostring(m, encoding='utf8')以查看正确的元素。

我个人更喜欢lxml到elementtree。假设您想通过'key'属性关联条目,您可以使用xpath索引其中一个文档,然后将它们拉入其他文档。

import lxml.etree

tree_english = lxml.etree.parse('english.xml')
tree_japanese = lxml.etree.parse('japanese.xml')

# index the japanese catalog
j_index = {}
for catalog in tree_japanese.xpath('MessageCatalogue/*[@key]'):
    j_index[catalog.get('key')] = catalog

# find catalog entries in english and merge the japanese
for catalog in tree_english.xpath('MessageCatalogue/*[@key]'):
    j_catalog = j_index.get(catalog.get('key'))
    if j_catalog is not None:
        print 'found match'
        for child in j_catalog:
            print 'add one'
            catalog.append(child)

print lxml.etree.tostring(tree_english, pretty_print=True, encoding='utf8')