我们在unix上有多个xml文件。我们需要将它们转换为平面文件。我们使用C解析了一个级别的xml文件(C被用作C可以与Teradata fastload进行通信,这是我们使用inmod的目标框,它将在一个解析中完成其他语言,我们需要做两次解析一个用于转换为平面文件,另一个用于加载ito teradata)。即以下文件
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
</book>
转换为
bk101~Gambardella, Matthew~XML Developer's Guide~Computer~44.95~
这是我们通过在C中解析文件来实现的。但是在看到下面的xml文件的原始格式之后。 (请不要将其视为必需的文件。我只是在提出一个想法)
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<modified>2010-01-02</modified>
<modified>2010-01-03</modified>
<price>44.95</price>
</book>
这应该转换为它看起来的两个记录。
bk101~Gambardella, Matthew~XML Developer's Guide~Computer~2010-01-02~44.95~
bk101~Gambardella, Matthew~XML Developer's Guide~Computer~2010-01-03~44.95~
但是现在我们觉得我们的C代码对于这个req来说会很复杂。所以我们正在寻找可以在unix上轻松使用的其他选项。任何人都可以为我们提供unix的不同语言/选项的任何工作示例代码吗?
答案 0 :(得分:3)
您可以使用XSLT。我使用可以在Unix上运行的Saxon(Java)。
此样式表处理两个XML样本:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/book">
<xsl:choose>
<xsl:when test="modified">
<xsl:for-each select="modified">
<xsl:call-template name="dump-line">
<xsl:with-param name="pos" select="position()"/>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@id"/><xsl:text>~</xsl:text>
<xsl:value-of select="author"/><xsl:text>~</xsl:text>
<xsl:value-of select="title"/><xsl:text>~</xsl:text>
<xsl:value-of select="genre"/><xsl:text>~</xsl:text>
<xsl:value-of select="price"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="dump-line">
<xsl:param name="pos"/>
<xsl:value-of select="/book/@id"/><xsl:text>~</xsl:text>
<xsl:value-of select="/book/author"/><xsl:text>~</xsl:text>
<xsl:value-of select="/book/title"/><xsl:text>~</xsl:text>
<xsl:value-of select="/book/genre"/><xsl:text>~</xsl:text>
<xsl:value-of select="/book/modified[$pos]"/><xsl:text>~</xsl:text>
<xsl:value-of select="/book/price"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
如果没有modified
元素,则输出一条记录。如果有modified
个元素,则会输出与modified
元素一样多的记录。
带有修改元素的示例输出:
bk101~Gambardella, Matthew~XML Developer's Guide~Computer~2010-01-02~44.95
bk101~Gambardella, Matthew~XML Developer's Guide~Computer~2010-01-03~44.95
答案 1 :(得分:1)
如果您要将数据加载到数据库中,并且您拥有与其他字段共享多对一关系的字段,那么您需要确保数据库结构最新。即书的一个表,以及修改日期的一个表。否则看起来有两本书,实际上有一本有两个修改日期。
但是,如果要将数据加载到数据库中,为什么要先将其转换为平面文件?你说你想避免两次通过解析。好吧,看起来你将有一个传递来解析XML并输出为平面文件,另一个传递来解析平面文件并将其输入数据库。为什么不简单地解析XMl并将数据直接放入数据库?
有理由发明像XML这样的格式,一种是在基于文本的文档中封装复杂的数据关系。通过转换为“平面文件”,您将失去这种复杂性。如果您要将数据导入到可以处理这种复杂性并存储这些关系的环境中......为什么不保留它?
您的数据库是否有API,或者只能导入平面文件?
--- --- EDIT
作为答案的一部分回复比回复一系列评论更容易。
首先,感谢您的澄清。 第二,不,我不能提供示例代码。主要是因为你想要的听起来非常具体。 第三,我认为你有两个选择:
1)您已经编写了大量C代码来解析XML。您必须考虑将它全部扔掉并在Perl中再次编写并支持它的成本,以及改进它以将数据直接导入Teradata数据库的成本以及此后维护它的成本。
2)对于Perl,有许多XML解析器,根据我的经验,它们比在C中更容易遍历XML树/数据结构。我不是Perl的粉丝,但是我已经编写了代码来处理在C中准备好解析的XML树,我从来都没有讨厌它。相比之下,在Perl中进行操作更简单,甚至可能更快。
有大量的Perl模块用于解析XML。我建议你在互联网上搜索一些关于它们的评论,以决定哪种评论最容易使用。
有一个名为Teradata :: SQL的Perl模块,它允许您将数据导入Teradata数据库。可能还有其他模块更容易/更简单/更好用。我没有任何经验,所以无法提出建议。在http://www.cpan.org搜索可能有用的任何模块。
最后,我强烈建议您确保花一些时间确保Teradata数据库的设计与进入其中的数据相匹配。如上所述,您在修改日期和书籍之间显然存在多对一关系,因此这意味着您需要一个用于修改日期的表格和一个用于书籍的表格,并在表格设计中更正多对一关系。每行放一个条目,导致同一本书的多行只有修改日期变化是非常错误的。可能存在其他多对一关系,例如作者。想象一下由作者A1和A2编写的书B,修改日期为M1和M2。如果您使用上面讨论过的方法,每个组合都有一行,那么您最终会为同一本书提供4个条目,看起来您有2本书具有相同的标题,但由不同的作者撰写。
花些时间确保您了解XML文件中数据的结构。这应该由DTD明确定义。
答案 2 :(得分:1)
XSLT是一种选择;查看xsltproc工具。
或者,您也可以使用更简单的XQuery,尽管您可能需要将其强制生成文本。以下XQuery脚本几乎可以执行您想要的操作(仅列出几个字段):
for $book in doc("book.xml")/book
for $mod in $book/modified
return concat($book/@id, "~", $book/title, "~", $mod, "
")
您可以使用
通过Saxon运行此功能java net.sf.saxon.Query '!method=text' script.xq
用于Unix的另一种流行的XQuery处理器是XQilla,但我不确定它是否可以产生非XML输出。
(对于我生成换行符的笨拙方式,可能有一个明智的选择。)
答案 3 :(得分:0)
如何将行格式化为bk101~Gambardella, Matthew~XML Developer's Guide~Computer~2010-01-02,2010-01-03~44.95~
。当然,必须特别考虑修改的字段可以包含值列表的事实。这与你能做到的一样平坦。