首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用XSLT组合两个XML文档,类似于SQL连接

如何使用XSLT组合两个XML文档,类似于SQL连接
EN

Stack Overflow用户
提问于 2013-03-09 04:04:01
回答 3查看 1.6K关注 0票数 1

我有一个适用于学校班级学生的数据库应用程序。我将大量数据放在一个包中发送到用户界面。要组装复杂的XML,我经常需要将多个数据获取作为XML,然后将它们组合在一起。

我正在尝试找到一种方法来使用XSLT执行类似于SQL连接的操作。例如,给定以下两个XML文档:

代码语言:javascript
运行
复制
<Xml>
    <Classes>
        <Class Name="BIOLOGY101" ClassId="11"/>
        <Class Name="PHYSICS101" ClassId="13"/>
        <Class Name="CALCULUS101" ClassId="17"/>
        <Class Name="BIOLOGY101" ClassId="19"/>
    </Classes>
</Xml>


<Xml>
    <Students>
        <Student Name="Bob Johnson" ClassId="11"/>
        <Student Name="Bob Johnson" ClassId="17"/>
        <Student Name="Bob Johnson" ClassId="19"/>
        <Student Name="Joe Jackson" ClassId="11"/>
        <Student Name="Joe Jackson" ClassId="13"/>
        <Student Name="Joe Jackson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="13"/>
        <Student Name="Rick Robertson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="19"/>
    </Students>
</Xml>

我想通过一个XSLT来运行它们,以生成this:

代码语言:javascript
运行
复制
<Xml>
    <Classes>
        <Class Name="BIOLOGY101" ClassId="11">
            <Student Name="Bob Johnson"/>
            <Student Name="Joe Jackson"/>
        </Class>
        <Class Name="PHYSICS101" ClassId="13">
            <Student Name="Joe Jackson"/>
            <Student Name="Rick Robertson"/>
        </Class>
        <Class Name="CALCULUS101" ClassId="17">
            <Student Name="Rick Robertson" "/>
            <Student Name="Joe Jackson"/>
            <Student Name="Bob Johnson"/>
        </Class>
        <Class Name="BIOLOGY101" ClassId="19">
            <Student Name="Bob Johnson"/>
            <Student Name="Rick Robertson" />
        </Class>
    </Classes>
</Xml>

注意,我在节点中省略了ClassId属性。

我可以将这两个XML文档组装成一个文档,然后传递给XSLT,如果这样做更容易处理的话。

由于此数据来自数据库,因此我将连接不同的XML文档。我可能会加入学校的班级,或者学生的成绩,或者学校的活动。但是它们都将遵循相同的模式:来自子节点的数字属性将对应于父节点中的数字属性。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-09 04:32:36

在XSLT中实现这一点并不像在SQL中那么简单,但是假设您事先将两个输入文件组合成了一个文档(如果对您没有问题的话,我建议您这样做):

代码语言:javascript
运行
复制
<Xml>
    <Classes>
        <Class Name="BIOLOGY101" ClassId="11"/>
        <Class Name="PHYSICS101" ClassId="13"/>
        <Class Name="CALCULUS101" ClassId="17"/>
        <Class Name="BIOLOGY101" ClassId="19"/>
    </Classes>
    <Students>
        <Student Name="Bob Johnson" ClassId="11"/>
        <Student Name="Bob Johnson" ClassId="17"/>
        <Student Name="Bob Johnson" ClassId="19"/>
        <Student Name="Joe Jackson" ClassId="11"/>
        <Student Name="Joe Jackson" ClassId="13"/>
        <Student Name="Joe Jackson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="13"/>
        <Student Name="Rick Robertson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="19"/>
    </Students>
</Xml>

此XSLT可用于将数据连接在一起:

代码语言:javascript
运行
复制
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
  <!-- Key to allow retrieving students by their class -->
  <xsl:key name="kStudentByClass" match="Student" use="@ClassId"/>

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

  <xsl:template match="Class">
    <xsl:copy>
      <!-- Copy all attributes for this Class element -->
      <xsl:apply-templates select="@*" />

      <!-- Copy in all students for the current class -->
      <xsl:apply-templates select="key('kStudentByClass', @ClassId)" />
    </xsl:copy>
  </xsl:template>

  <!-- Omit the Students element and Students' ClassId 
       attribute from the output -->
  <xsl:template match="Students | Student/@ClassId" />
</xsl:stylesheet>

当在上面的输入XML上运行此命令时,它会生成:

代码语言:javascript
运行
复制
<Xml>
  <Classes>
    <Class Name="BIOLOGY101" ClassId="11">
      <Student Name="Bob Johnson" />
      <Student Name="Joe Jackson" />
    </Class>
    <Class Name="PHYSICS101" ClassId="13">
      <Student Name="Joe Jackson" />
      <Student Name="Rick Robertson" />
    </Class>
    <Class Name="CALCULUS101" ClassId="17">
      <Student Name="Bob Johnson" />
      <Student Name="Joe Jackson" />
      <Student Name="Rick Robertson" />
    </Class>
    <Class Name="BIOLOGY101" ClassId="19">
      <Student Name="Bob Johnson" />
      <Student Name="Rick Robertson" />
    </Class>
  </Classes>
</Xml>

如果您可以稍微更改XML以指示外部组和内部组,以及要匹配的属性,如下所示:

代码语言:javascript
运行
复制
<Xml>
  <Classes outer="true" matchAttribute="ClassId">
     ....
  </Classes>
  <Students inner="true">
     ....
  </Students>
</Xml>

然后,您可以使用这个更通用的XSLT,它虽然效率较低,但应该适用于任何类似于上面的输入:

代码语言:javascript
运行
复制
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:variable name="innerItems"
                select="/*/*[@inner = 'true']/*" />
  <xsl:variable name="matchAttribute" 
                select="/*/*[@outer = 'true']/@matchAttribute" />

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

  <xsl:template match="/*/*[@outer = 'true']/*">
    <xsl:copy>
      <!-- Copy all attributes for this element -->
      <xsl:apply-templates select="@*" />

      <xsl:variable name="matchValue" 
                    select="@*[local-name() = $matchAttribute]"/>
      <!-- Copy in all matching items -->
      <xsl:apply-templates 
        select="$innerItems[@*[local-name() = $matchAttribute] =
                      $matchValue]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*/*[@inner = 'true']/*">
    <xsl:copy>
      <xsl:apply-templates 
         select="@*[local-name() != $matchAttribute] | node()" />
    </xsl:copy>
  </xsl:template>

  <!-- Omit the inner group element, and the meta attributes-->
  <xsl:template match="/*/*[@inner = 'true'] | @outer | @matchAttribute" />
</xsl:stylesheet>
票数 3
EN

Stack Overflow用户

发布于 2013-03-09 04:45:48

JLRishe的答案是正确的,并且可以完美地工作,但是如果您需要使用XSLT将这两个文件组合在一起,您可以这样做,以防其他人出现

a.xml

代码语言:javascript
运行
复制
<Xml>
    <Classes>
        <Class Name="BIOLOGY101" ClassId="11"/>
        <Class Name="PHYSICS101" ClassId="13"/>
        <Class Name="CALCULUS101" ClassId="17"/>
        <Class Name="BIOLOGY101" ClassId="19"/>
    </Classes>
</Xml>

b.xml

代码语言:javascript
运行
复制
<Xml>
    <Students>
        <Student Name="Bob Johnson" ClassId="11"/>
        <Student Name="Bob Johnson" ClassId="17"/>
        <Student Name="Bob Johnson" ClassId="19"/>
        <Student Name="Joe Jackson" ClassId="11"/>
        <Student Name="Joe Jackson" ClassId="13"/>
        <Student Name="Joe Jackson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="13"/>
        <Student Name="Rick Robertson" ClassId="17"/>
        <Student Name="Rick Robertson" ClassId="19"/>
    </Students>
</Xml>

样式表

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="utf-8"?>

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

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

  <xsl:template match="Class">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <!-- Apply <Student> elements from b.xml -->
      <xsl:apply-templates
        select="document('b.xml')/Xml/Students/Student
          [@ClassId = current()/@ClassId]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Student">
    <xsl:copy>
      <xsl:apply-templates select="@Name"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

输出

代码语言:javascript
运行
复制
<?xml version="1.0"?>
<Xml>
  <Classes>
    <Class Name="BIOLOGY101" ClassId="11">
      <Student Name="Bob Johnson"/>
      <Student Name="Joe Jackson"/>
    </Class>
    <Class Name="PHYSICS101" ClassId="13">
      <Student Name="Joe Jackson"/>
      <Student Name="Rick Robertson"/>
    </Class>
    <Class Name="CALCULUS101" ClassId="17">
      <Student Name="Bob Johnson"/>
      <Student Name="Joe Jackson"/>
      <Student Name="Rick Robertson"/>
    </Class>
    <Class Name="BIOLOGY101" ClassId="19">
      <Student Name="Bob Johnson"/>
      <Student Name="Rick Robertson"/>
    </Class>
  </Classes>
</Xml>
票数 3
EN

Stack Overflow用户

发布于 2013-03-09 09:41:28

JLRishe的回答很棒。在我得到它之前我迷失了方向。(我发布了这个问题。)

因为我知道我正在处理来自SQL Server的数据,所以我将始终拥有与两个表相对应的父节点和子节点,并且连接它们的属性是来自这两个表的字段:一个具有主键字段的独立表和一个具有外键字段的依赖表。因此,我将针对我的特定需求的解决方案概括如下:

代码语言:javascript
运行
复制
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>


    <!-- Parameters to make solution general -->
    <xsl:param name="ParentNode">Class</xsl:param>
    <xsl:param name="ParentIdAttribute">ClassId</xsl:param>
    <xsl:param name="ChildNode">Student</xsl:param>
    <xsl:param name="ChildReferenceAttribute">ClassId</xsl:param>

    <!-- Key to allow retrieving child nodes by the Parent node -->
    <xsl:key name="kChildByParent" match="*[name() = $ChildNode]" use="@*[name()=$ChildReferenceAttribute]"/>

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


    <!-- Parent Node Matching Template -->
    <xsl:template match="*[name() = $ParentNode]">
        <xsl:copy>
            <!-- Copy all attributes and child nodes for the Parent element -->
            <xsl:apply-templates select="@* | node()" />

            <!-- Copy in all children that reference the current parent -->
            <xsl:apply-templates select="key('kChildByParent', @*[name()=$ParentIdAttribute])" />
        </xsl:copy>
    </xsl:template>

    <!-- Omit the child nodes and reference attribute of the child nodes, -->
    <xsl:template match="ChildNodes | *[name() = $ChildNode]/@*[name()=$ChildReferenceAttribute]" />

</xsl:stylesheet>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15302356

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档