我需要将巨大的 XML文档转换为多个HTML文档。 XML如下:
<society>
<party_members>
<member id="1" first_name="" last_name="O'Brien">
<ministry_id>1</ministry_id>
<ministry_id>3</ministry_id>
</member>
<member id="2" first_name="Julia" last_name="">
<ministry_id>2</ministry_id>
</member>
<member id="3" first_name="Winston" last_name="Smith">
<ministry_id>1</ministry_id>
</member>
</party_members>
<ministries>
<ministry>
<id>1</id>
<short_title>Minitrue</short_title>
<long_title>Ministry of truth</long_title>
<concerns>News, entertainment,education and arts </concerns>
</ministry>
<ministry>
<id>2</id>
<short_title>Minipax</short_title>
<long_title>Ministry of Peace</long_title>
<concerns>War</concerns>
</ministry>
<ministry>
<id>3</id>
<short_title>Minilove</short_title>
<long_title>Ministry of Love</long_title>
<concerns>Dissidents</concerns>
</ministry>
</ministries>
</society>
如果潜在的党员人数可能很大 - 数百万,而且部委数量很少,大约300-400。对于每个党员,应该有一个输出HTML,其中包含以下内容:
<html>
<body>
<h2>Party member: Winston Smith</h2>
<h3>Works in:</h3>
<div class="ministry">
<h4>Ministry of truth</h4> - Minitrue
<h5>Ministry of truth <i>concerns</i> itself with <i>News, entertainment,education and arts</i></h5>
</div>
</body>
</html>
输出文件的数量应= =党员人数。
我现在正在努力使用XSLT,但无法让它发挥作用。
请帮我判断XSLT是否是这项工作的好工具,如果是的话,提示我好像如何实现它,应该使用什么XSLT构造等等。
当然我可以简单地用过程语言编写迷你转换,但我正在寻找一种'应用转换模板'方法,而不是程序解析和修改以便能够提供模板其他用户进行进一步修改(CSS,格式化等)。
我正在使用ruby + nokogiri(这是一组绑定到libxslt),但可以使用任何语言。
如果XSTL不适合这项任务,那么在这里可以使用哪些其他工具,前提是我必须在几分钟内转换大约1M的用户并消耗少量内存?
额外的好处是能够并行化处理。
谢谢。
答案 0 :(得分:3)
使用纯XSLT 1.0,您无法使用您似乎想要执行的单个转换创建多个结果文档。为此,您需要使用XSLT 2.0处理器(如Saxon 9或AltovaXML)和XSLT 2.0指令[xsl:result-document][1]
,或者您需要使用XSLT 1.0处理器,如xsltproc / libxslt,它实现http://www.exslt.org/exsl/elements/document/index.html。如果您可以使用其中一个,那么XSLT非常适合您的任务。
[编辑] 使用libxslt和xsltproc分别使用以下样式表代码
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
extension-element-prefixes="exsl"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:key name="ministry-by-id" match="ministry" use="id"/>
<xsl:template match="/">
<xsl:apply-templates select="society/party_members/member" mode="doc"/>
</xsl:template>
<xsl:template match="member" mode="doc">
<exsl:document href="member{@id}.xml">
<html>
<body>
<h2>Party member: <xsl:value-of select="concat(@first_name, ' ', @last_name)"/></h2>
<h3>Works in</h3>
<xsl:apply-templates select="key('ministry-by-id', ministry_id)"/>
</body>
</html>
</exsl:document>
</xsl:template>
<xsl:template match="ministry">
<div class="ministry">
<h4><xsl:value-of select="long_title"/></h4>
<h5><xsl:value-of select="long_title"/> <i>concerns</i> itself with <i><xsl:value-of select="concerns"/></i></h5>
</div>
</xsl:template>
</xsl:stylesheet>
显示如何使用exsl:document
通过一次转换输出多个结果文档。它还使用密钥来提高性能。让我们知道该代码是否适用于您的大量输入数据。
答案 1 :(得分:2)
为了实现这个结果(生成几个html文件),你肯定需要XSLT 2.0。我建议使用Saxon。
这里有一个示例XSL,可以生成您需要的内容(为每个成员创建一个单独的html文件,都在系统根目录下的“html”文件夹中,并返回它创建的内容的报告)。您可能需要稍微调整一下以满足您的需求。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" method="html"/>
<xsl:variable name="target-dir" select="'/html'"/>
<xsl:key name="ministries" match="/society/ministries/ministry" use="id"/>
<xsl:strip-space elements="*"/>
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<xsl:template match="/">
<Output>
<xsl:apply-templates select="*"/>
</Output>
</xsl:template>
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<xsl:template match="*">
<xsl:apply-templates select="*"/>
</xsl:template>
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<xsl:template match="member">
<Html path="{concat($target-dir,'/',@id,'.html')}">
<xsl:result-document href="{concat($target-dir,'/',@id,'.html')}">
<html>
<body>
<h2><xsl:value-of select="concat('Party member: ',@first_name,' ',@last_name)"/></h2>
<h3>Works in:</h3>
<xsl:apply-templates select="ministry_id"/>
</body>
</html>
</xsl:result-document>
</Html>
</xsl:template>
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<xsl:template match="ministry_id">
<xsl:variable name="ministry" select="key('ministries',.)"/>
<div class="ministry">
<h4><xsl:value-of select="$ministry/long_title"/></h4> - <xsl:value-of select="$ministry/short_title"/>
<h5><xsl:value-of select="$ministry/long_title"/> <i>concerns</i> itself with <i><xsl:value-of select="$ministry/concerns"/></i></h5>
</div>
</xsl:template>
</xsl:stylesheet>
这里有一个示例输出:
<html>
<body>
<h2>Party member: O'Brien</h2>
<h3>Works in:</h3>
<div class="ministry">
<h4>Ministry of truth</h4> - Minitrue
<h5>Ministry of truth<i>concerns</i> itself with <i>News, entertainment,education and arts </i></h5>
</div>
<div class="ministry">
<h4>Ministry of Love</h4> - Minilove
<h5>Ministry of Love<i>concerns</i> itself with <i>Dissidents</i></h5>
</div>
</body>
</html>
关于性能,数百万是大量数据。我想xsl就足够了,但我担心在确定之前你需要先尝试一下。
我希望这可以帮到你!
答案 2 :(得分:1)
使用XSLT编写所需的转换应该很简单,但我不认为一次性处理如此大的XML是正确的技术:它会将整个数据加载到内存中并从那里开始工作,而不是这么大的数据集很好用。
如果每个HTML文档对应于整个文档的一个小的连续部分,我建议使用程序(使用不在内存中加载整个文档的XML解析器)拆分大XML文件,然后转换每个部分都使用XSLT。
如果每个HTML文档都包含来自文件和/或聚合的不同部分的数据(例如,符合某些条件的成员总数),我建议解析XML并将其数据加载到SQL数据库中,然后生成从该数据库读取的HTML文件。
答案 3 :(得分:1)
大部分工作肯定可以在XSLT 1.0中完成,尽管Martin说,你只能生成一个包含所有HTML exerpts的文档。由此,您可以使用XPath技术选择每个html节点并返回每个节点的OuterXml,以便您写入文件/数据库等。
然而,由于内存限制,可能无法立即处理所有党员。
由于党员对其他成员(仅对部委)没有依赖,我建议你一次处理1000名左右的党员,所有部门都在XML文件中。您还可以将各部门拆分为单独的xml文件,并在处理每个成员xml文件期间使用xsl:document
加载各部。
修改:xsl:document reference和使用示例here
以下XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:output method="xml" omit-xml-declaration="no" indent="yes" />
<xsl:template match="/society">
<root>
<xsl:apply-templates select="party_members/member" />
</root>
</xsl:template>
<xsl:template match="member">
<html>
<body>
<h2>
Party member: <xsl:value-of select="@first_name"/><xsl:text xml:space="preserve"> </xsl:text><xsl:value-of select="@last_name"/>
</h2>
<h3>Works in:</h3>
<div class="ministry">
<xsl:for-each select="ministry_id">
<xsl:variable name="ministryId" select="./text()" />
<xsl:apply-templates select="/society/ministries/ministry[id=$ministryId]" mode="partymember"/>
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="ministry" mode="partymember">
<h4>
<xsl:value-of select="long_title"/>
</h4> - <xsl:value-of select="short_title"/>
<h5>
<xsl:value-of select="long_title"/><i>concerns</i> itself with <i>
<xsl:value-of select="concerns"/>
</i>
</h5>
</xsl:template>
</xsl:stylesheet>
产生以下输出(在漂亮打印之后):
<?xml version="1.0" encoding="utf-8"?>
<root>
<html>
<body>
<h2>
Party member: O'Brien
</h2>
<h3>Works in:</h3>
<div class="ministry">
<h4>Ministry of truth</h4> - Minitrue<h5>
Ministry of truth<i>concerns</i> itself with <i>News, entertainment,education and arts </i>
</h5>
</div>
</body>
</html>
<html>
<body>
<h2>
Party member: Julia
</h2>
<h3>Works in:</h3>
<div class="ministry">
<h4>Ministry of Peace</h4> - Minipax<h5>
Ministry of Peace<i>concerns</i> itself with <i>War</i>
</h5><h4>Ministry of Love</h4> - Minilove<h5>
Ministry of Love<i>concerns</i> itself with <i>Dissidents</i>
</h5>
</div>
</body>
</html>
<html>
<body>
<h2>
Party member: Winston Smith
</h2>
<h3>Works in:</h3>
<div class="ministry">
<h4>Ministry of truth</h4> - Minitrue<h5>
Ministry of truth<i>concerns</i> itself with <i>News, entertainment,education and arts </i>
</h5>
</div>
</body>
</html>
</root>
答案 4 :(得分:1)
“巨大”有多大?如果有一百万成员,我对Saxon的TinyTree大小的猜测大约是100Mb,这在内存中肯定是可行的。但是你可能危险地接近无法进行主内存转换的点,然后你必须考虑流式转换。
幸运的是,这不会使XSLT无法使用,但它确实限制了您可能的XSLT处理器范围。
Saxon-EE支持基于草案XSLT 3.0规范的流转换,但是,您经常需要以稍微不同的方式编写代码。在此示例中,要使用流式传输,您首先需要将“部门”数据拆分为单独的文件 - 您可以将拆分作为包含两个结果文档的流式转换。然后在主转换中,您可以使用Carles Sala建议的密钥,对“成员”数据进行流处理,同时对“部门”数据进行内存处理。
Streaming XSLT是最前沿的技术,所以我们总是非常愿意帮助那些决定试用它的用户。