使用XSLT组合多个XML文件并执行一些数学运算

时间:2014-05-18 13:29:46

标签: xml xslt merge xslt-2.0

我有一个XML文件 authors.xml ,如下所示:

<?xml version="1.0" encoding="ISO-8859-1"?>
<authors>
  <author>
    <name>Leonardo da Vinci</name>
    <nationality>Italian</nationality>
  </author>
  <author>
    <name>Pablo Picasso</name>
    <nationality>Spanish</nationality>
  </author>
</authors>

和另一个列出其作品 artwork.xml 的文件如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<artworks>
  <artwork>
    <title>Mona Lisa</title>
    <author>Leonardo da Vinci</author>
    <date>1497</date>
    <form>painting</form>
  </artwork>
  <artwork>
    <title>Vitruvian Man</title>
    <author>Leonardo da Vinci</author>
    <date>1499</date>
    <form>painting</form>
  </artwork>
  <artwork>
    <title>Absinthe Drinker</title>
    <author>Pablo Picasso</author>
    <date>1479</date>
    <form>painting</form>
  </artwork>
  <artwork>
    <title>Chicago Picasso</title>
    <author>Pablo Picasso</author>
    <date>1950</date>
    <form>sculpture</form>
  </artwork>
</artworks>

我希望将这两个XML文件合并到另一个处理过的XML文件中。 XSLT将列出所有作者,并在其中列出与该特定作者相关的所有艺术作品,并按艺术作品形式对其进行分组。 XSLT还将计算图稿组的数量。组的持续时间也作为元素属性添加。这在下面的XML文件中进一步说明:

<?xml version="1.0" encoding="UTF-8" ?>
<authors>
  <author>
    <name>Leonardo da Vinci</name>
    <nationality>Italian</nationality>
    <artworks form="painting" duration="1497-1499" quantity="2">
      <artwork date="1497">
        <title>Mona Lisa</title>
      </artwork>
      <artwork date="1499">
        <title>Vitruvian Man</title>
      </artwork>
    </artworks>
  </author>
  <author>
    <name>Pablo Picasso</name>
    <nationality>Spanish</nationality>
    <artworks form="painting" duration="1479-1479" quantity="1">
      <artwork date="1479">
        <title>Absinthe Drinker</title>
      </artwork>
    </artworks>
    <artworks form="sculpture" duration="1950-1950" quantity="1">
      <artwork date="1950">
        <title>Chicago Picasso</title>
      </artwork>
    </artworks>
  </author>
</authors>

我还是新手。我设法做的是获取所有作者的部分,现在我不确定如何从其他XML文件中提取数据,同时还要计算艺术品的出现等等。我在程序编程方面非常有经验,比如C或C ++,但这种声明式编程方法真的让我头脑发热!希望有人可以指出我正确的方向,以便我能做到这一点。

1 个答案:

答案 0 :(得分:5)

此样式表将生成您期望的输出,使用authors.xml文件作为输入源,并将artworks.xml放在同一目录中:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes"/>

    <xsl:variable name="artworks" select="doc('artworks.xml')/artworks"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="authors/author">
        <xsl:copy>
            <xsl:copy-of select="name"/>
            <xsl:copy-of select="nationality"/>
            <xsl:for-each-group 
                select="$artworks/artwork[author=current()/name]"
                group-by="form">

                <artworks form="{form}" 
                    duration="{min(current-group()/date)}-{max(current-group()/date)}" 
                    quantity="{count(current-group())}">
                    <xsl:apply-templates select="current-group()"/>
                </artworks>

            </xsl:for-each-group>  
        </xsl:copy>
    </xsl:template>

    <xsl:template match="artwork">
        <artwork date="{date}">
            <title><xsl:value-of select="title"/></title>
        </artwork>
    </xsl:template>

</xsl:stylesheet>

以下是对上述代码的解释:

我使用xsl:variable来引用导入文档中的artworks子树:

<xsl:variable name="artworks" select="doc('artworks.xml')/artworks"/>

此模板是一个标识转换,它将匹配任何节点和属性并将其复制到输出。它的优先级低于其他两个模板,因此只有在其他模板不匹配时才会调用它:

<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>

第二个模板必须与authors/author匹配(而不是author,因为在处理两个文档时都会调用它,而author内还有另一个artworkcopy-of元素复制所选节点的整个子树(元素,内容和属性)。

<xsl:template match="authors/author">
    <xsl:copy>
        <xsl:copy-of select="name"/>
        <xsl:copy-of select="nationality"/>
        ...
    </xsl:copy>
</xsl:template>

for-each-group对来自artwork文件的每个artworks.xml元素进行迭代,该文件与输入中当前节点的name元素具有相同的author文件(authors.xml)。它按form分组。您使用current-group()来引用当前组,您需要计算maxmin日期,计算数量并打印<artwork>个节点。

<xsl:for-each-group 
    select="$artworks/artwork[author=current()/name]"
    group-by="form">

    <artworks form="{form}" 
        duration="{min(current-group()/date)}-{max(current-group()/date)}" 
        quantity="{count(current-group())}">
        <xsl:apply-templates select="current-group()"/>
    </artworks>

</xsl:for-each-group> 

最后,此模板格式化每个artwork节点:

<xsl:template match="artwork">
    <artwork date="{date}">
        <title><xsl:value-of select="title"/></title>
    </artwork>
</xsl:template>

可以以不同的方式完成所有这些操作,在单个根/匹配模板和几个嵌套的for-each块中,但在编码时使用模板是一个更好的做法XSLT。