3

我需要将一个巨大的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、格式ETC)。

我正在使用 ruby​​ + nokogiri(这是一组与 libxslt 的绑定),但可以使用任何语言。

如果 XSTL 不适合这项任务,那么在这里可以使用哪些其他工具,前提是我必须在几分钟内转换约 1M 的用户,并且内存消耗很少?

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

谢谢你。

4

5 回答 5

3

好吧,使用纯 XSLT 1.0,您无法使用您似乎想要做的单个转换来创建多个结果文档。为此,您需要使用 XSLT 2.0 处理器(如 Saxon 9 或 AltovaXML)和 XSLT 2.0 指令[xsl:result-document][1],或者您需要使用 XSLT 1.0 处理器(如实现http://www.exslt.org/exsl/elements/document的 xsltproc/libxslt) /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一个转换输出多个结果文档。它还使用密钥来提高性能。让我们知道该代码是否适用于您的大量输入数据。

于 2012-10-25T12:04:22.187 回答
2

为了达到这个结果(生成几个 html 文件),您肯定需要 XSLT 2.0。我建议为此使用撒克逊语。

在这里,您有一个示例 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 就足够了,但恐怕你需要先试一试才能确定。

我希望这可以帮助你!

于 2012-10-25T12:29:37.413 回答
1

使用 XSLT 编写所需的转换应该很简单,但我认为一次性处理如此大的 XML 不是正确的技术:它将整个数据加载到内存中并从那里工作,这对这么大的数据集。

如果每个 HTML 文档对应于整个文档的一个小的连续部分,我建议使用程序(使用不会将整个文档加载到内存中的 XML 解析器)拆分大 XML 文件,然后使用一个 XSLT。

如果每个 HTML 文档包含来自文件和/或聚合的不同部分的数据(例如匹配某些条件的成员总数),我建议解析 XML 并将其数据加载到 SQL 数据库中,然后生成 HTML从此数据库中读取的文件。

于 2012-10-25T12:04:13.900 回答
1

大部分工作肯定可以在 XSLT 1.0 中完成,尽管正如 Martin 所说,您只能生成一个包含所有 HTML 摘录的文档。由此,您可以使用 XPath 技术选择每个 html 节点并返回每个节点的 OuterXml,以便您将其写入文件/数据库等。

然而,由于内存限制,一次处理所有党员可能不可行。

由于党员不依赖于其他成员(仅依赖于部委),我建议您一次处理 1000 个左右的党员,所有部委都存在于 XML 文档中。您还可以将部委拆分为单独的 xml 文件,并xsl:document在处理每个党员 xml 文件期间用于加载部委。

编辑xsl:document reference和使用示例在这里

以下 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>
于 2012-10-25T12:17:11.880 回答
1

“巨大”有多大?如果有 100 万个成员,我猜测 Saxon 的 TinyTree 的大小约为 100Mb,这在内存中肯定是可行的。但是您可能非常危险地接近无法进行主内存转换的地步,然后您必须考虑进行流式转换。

幸运的是,这并没有使使用 XSLT 成为不可能,尽管它确实限制了您可能使用的 XSLT 处理器的范围。

Saxon-EE 支持基于 XSLT 3.0 规范草案的流转换,但是,您通常必须以稍微不同的方式编写代码。在此示例中,要使用流式传输,您首先需要将“部委”数据拆分到一个单独的文件中 - 您可以将拆分作为带有两个结果文档的流式转换进行。然后在主转换中,您可以使用 Carles Sala 建议的键对“成员”数据进行流式处理,以及对“部委”数据进行内存处理。

流式 XSLT 是尖端技术,因此我们总是非常愿意帮助决定尝试它的用户。

于 2012-10-25T15:20:24.667 回答