我需要解析许多XML文件。我写了一些有用的代码,但很难看,我想从比我更有经验的人那里得到一些建议。
首先,我可能在错误的上下文中使用了一些术语,因为我对XML的经验有限。按元素,除非另有说明,我的意思是这样的:
<root>
<element>
...
</element>
<element>
...
</element>
</root>
无论如何,每个文件都包含许多元素,并带有许多子元素(显然)。让我感到困惑的是,需要以四种不同的方式获取相关值;
1)节点文字:
<tag>value</tag>
2)属性:
<tag attribute="value"></tag>
3)标签内“隐藏”的值(本例中为“true”):
<tag><boolean.true/></tag>
4)同名的标签内的值(“tagA”),但带有不同名称的“祖父母”标签(“tag1”和“tag2”),都在同一个元素内。 “tagA”对我没用,相反我会寻找“tag1”和“tag2”。
<element>
<tag1><tagA>value</tagA><tag1>
<tag2><tagA>value</tagA></tag2>
</element>
目前我有一个字典,每个文件都作为一个键。值是带有“属性”,“节点文本”,“标记”和“父元素”键的字典。
示例:
{'file1.xml' : 'attributes' : {'Person': 'Id', 'Car' : 'Color'},
'node text': ['Name', 'Address'],
}
“人”和“汽车”是标签,“Id”和“颜色”是属性名称。
这样可以轻松迭代所有元素并检查每个标记,如果字典中存在匹配项(如果dict ['file1.xml'] ['attributes']中的elem.tag),则提取值
正如我所说,代码有效,但我不喜欢我的解决方案。此外,并非所有元素都具有所有子元素(例如,Person可能没有汽车,那么该标签将完全丢失),我需要将这些值分配给“None”。现在我得到每个文件中每个元素应该存在的所有标记,将它们变成一个集合,然后检查它们与我实际从该元素中提取值的标记集之间的差异。再一次,代码非常难看。
希望这个混乱有道理。
编辑:
我使用J.F.Sebastian建议将xpath存储到字典中的每个值,字段名称为键,xpath为值。
答案 0 :(得分:3)
您可以使用相对于您的元素的xpath
表达式来简化您的输入代码,而不是复杂的数据结构,例如#1-4个案例:
要使用的输出数据结构取决于您希望以后如何在代码中使用它。您可以从对当前代码最方便的结构开始。当您更好地理解需求时,将其演变为更通用的解决方案。
我将它输出到csv,其中每个元素都是csv文件中的一行。 ... 我使用defaultdict存储元素,然后在将它们输出到csv之前将它们存储在列表中。
你可以使用普通的dict和csv.DictWriter(fieldnames = xpathdict.keys()):
# for each element
row_dict = dict.fromkeys(xpathdict.keys())
...
# for each key
row_dict[key] = element.xpath(xpathdict[key]) or None
...
dictwriter.writerow(row_dict)
其中xpathdict
是字段名称和相应的xpath表达式之间的映射。一般而言,除了xpath exprs之外,您还可以存储函数对象f(element) -> csv field
,而不是/。
答案 1 :(得分:2)
我不认为#3是合法的XML,因为没有关联的开放标记,即使它在其他地方,也不会在该示例中正确嵌套。由于<
字符,表达式将被解释为结束标记。
答案 2 :(得分:1)
我假设你想要采取这样的事情:
<root>
<element>
<text_attribute>Some Text</text_attribute>
<attribute var="blah"/>
<bool_attribute><boolean.true/></bool_attribute>
</element>
<element>
<text_attribute>Some more Text</text_attribute>
<attribute var="blah again"/>
<bool_attribute><boolean.false/></bool_attribute>
</element>
</root>
得到这样的东西:
[
{ "text_attribute":"Some Text", "attribute":"blah", "bool_attribute":True },
{ "text_attribute":"Some more Text", "attribute":"blah again", "bool_attribute":False }
]
要做到这一点,我会做这样的事情(未经测试):
# Helper function so we can extract a default from an xpath result if empty
def get_first(x, default_value):
if(len(x)>0) return x[0]
return default_value
# Parse one element
def process_element( e ):
retval = {}
retval['text_attribute'] = get_first(e.xpath("text_attribute/text()"), "default text")
retval['attribute'] = get_first( e.xpath("attribute/@var"), "default attribute")
retval['bool_attribute'] = get_first( e.xpath("bool_attribute/boolean.true"), False )
return retval
# Parse all the elements
elements = []
elements_xml = xml.xpath('/root/element')
for e in elements_xml:
elements.push( process_element(e) )