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

sam*_*ude 1 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>
Run Code Online (Sandbox Code Playgroud)

和另一个文件列出他们的艺术品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>
Run Code Online (Sandbox Code Playgroud)

我想要做的是将这两个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>
Run Code Online (Sandbox Code Playgroud)

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

hel*_*cha 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>
Run Code Online (Sandbox Code Playgroud)

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

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

<xsl:variable name="artworks" select="doc('artworks.xml')/artworks"/>
Run Code Online (Sandbox Code Playgroud)

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

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

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

<xsl:template match="authors/author">
    <xsl:copy>
        <xsl:copy-of select="name"/>
        <xsl:copy-of select="nationality"/>
        ...
    </xsl:copy>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

for-each-group每个迭代上artwork从元件artworks.xml文件具有相同nameauthor从输入文档中的当前节点的元素(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> 
Run Code Online (Sandbox Code Playgroud)

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

<xsl:template match="artwork">
    <artwork date="{date}">
        <title><xsl:value-of select="title"/></title>
    </artwork>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

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