XSLT - 适合任务吗?

时间:2012-10-25 11:50:47

标签: xml xslt parallel-processing transformation text-processing

我需要将巨大的 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的用户并消耗少量内存?

额外的好处是能够并行化处理。

谢谢。

5 个答案:

答案 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是最前沿的技术,所以我们总是非常愿意帮助那些决定试用它的用户。