在平面XML文件中创建结构
我有一个这样的xml文件:
<car>Ferrari</car>
<color>red</color>
<speed>300</speed>
<car>Porsche</car>
<color>black</color>
<speed>310</speed>
我需要把它变成这个样子:
<car name="Ferrari">
<color>red</color>
<speed>300</speed>
</car>
<car name="Porsche">
<color>black</color>
<speed>310</speed>
</car>
我该怎么做呢?我遇到困难了,因为我想不到如何从原始xml文件中平坦的标签列表创建我需要的结构。
我选择的编程语言是Python,但任何建议我都欢迎。
4 个回答
0
如果你的实际数据和你的例子一样简单,而且没有任何错误,你可以用正则表达式替换一次性搞定:
import re
guff = """
<car>Ferrari</car>
<color>red</color>
<speed>300</speed>
<car>Porsche</car>
<color>black</color>
<speed>310</speed>
"""
pattern = r"""
<car>([^<]+)</car>\s*
<color>([^<]+)</color>\s*
<speed>([^<]+)</speed>\s*
"""
repl = r"""<car name="\1">
<color>\2</color>
<speed>\3</speed>
</car>
"""
regex = re.compile(pattern, re.VERBOSE)
output = regex.sub(repl, guff)
print output
否则,你最好每次读取三行数据,进行一些验证,然后一个一个地写出“车”元素,可以使用字符串处理或者ElementTree来完成。
8
XSLT 是一个非常适合把一种 XML 结构转换成另一种的工具。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- copy the root element and handle its <car> children -->
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="car" />
<xsl:copy>
</xsl:template>
<!-- car elements become a container for their properties -->
<xsl:template match="car">
<car name="{normalize-space()}">
<!-- ** see 1) -->
<xsl:copy-of select="following-sibling::color[1]" />
<xsl:copy-of select="following-sibling::speed[1]" />
</car>
</xsl:template>
</xsl:stylesheet>
1) 要让这个转换顺利进行,你的 XML 里每个 <car>
都必须有 <color>
和 <speed>
这两个部分。如果这两个部分不一定存在,或者属性的数量和种类可能会变化,那就把这两行代码换成更通用的复制语句:
<!-- any following-sibling element that "belongs" to the same <car> -->
<xsl:copy-of select="following-sibling::*[
generate-id(preceding-sibling::car[1]) = generate-id(current())
]" />
如果应用到你的 XML(我假设有一个名为 <root>
的根元素),那么结果就是这样:
<root>
<car name="Ferrari">
<color>red</color>
<speed>300</speed>
</car>
<car name="Porsche">
<color>black</color>
<speed>310</speed>
</car>
</root>
在 Python 中应用 XSLT 到 XML 的示例代码其实很容易找到,所以我这里就不写了。通常也就四五行 Python 代码而已。
1
我对Python不太了解,不过假设你有一个可以解析XML的工具,它能让你以层级的方式访问XML文档中的节点。你想要的逻辑大概是这样的(警告,我通常用PHP)。基本上,你需要存储任何不是“car”的标签,然后当你遇到一个新的“car”标签时,把它当作一个分隔符,创建一个组合好的XML节点:
// Create an input and output handle
input_handle = parse_xml_document();
output_handle = new_xml_document();
// Assuming the <car>, <color> etc. nodes are
// the children of some, get them as an array
list_of_nodes = input_handle.get_list_child_nodes();
// These are empty variables for storing our data as we parse it
var car, color, speed = NULL
foreach(list_of_nodes as node)
{
if(node.tag_name() == "speed")
{
speed = node.value();
// etc for each type of non-delimiting field
}
if(node.tag_name() == "car")
{
// If there's already a car specified, take its data,
// insert it into the output xml structure and th
if(car != NULL)
{
// Add a new child node to the output document
node = output_handle.append_child_node("car");
// Set the attribute on this new output node
node.set_attribute("name", node.value());
// Add the stored child attributes
node.add_child("color", color);
node.add_child("speed", speed);
}
// Replace the value of car afterwards. This allows the
// first iteration to happen when there is no stored value
// for "car".
car = node.value();
}
}