我为学校的学生提供了一个数据库应用程序。我将一个包中的大量数据发送到用户界面。为了组装复杂的XML,我经常需要将多个数据提取作为XML,然后将它们组合起来。
我正在尝试找到一种方法来使用XSLT来执行类似于SQL JOIN的操作。例如,给定以下两个XML文档:
<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运行它们来生成这个:
<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>
请注意,我从&lt; Student&gt;中省略了ClassId属性。节点
我可以将两个XML文档组合到一个文档中以传递到XSLT,如果这样可以更容易处理。
由于这些数据来自数据库,我将加入不同的XML文档。我可能会加入课程,学生或学生活动。但它们都将遵循相同的模式:子节点中的数字属性将对应于父节点中的数字属性。
答案 0 :(得分:3)
在XSLT中完成此操作并不像在SQL中那样简单,但假设您提前将两个输入文件组合到一个文档中(如果对您没有问题,我建议这样做):
<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可用于将数据连接在一起:
<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上运行时,它会产生:
<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以指示外部和内部组,以及要匹配的属性,请执行以下操作:
<Xml>
<Classes outer="true" matchAttribute="ClassId">
....
</Classes>
<Students inner="true">
....
</Students>
</Xml>
然后你可以使用这个更通用的XSLT,虽然效率较低,但应该适用于与上述类似的任何输入:
<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>
答案 1 :(得分:3)
JLRishe的答案是正确的,并且完美无缺,但是万一其他人来看看,如果你需要使用XSLT来组合这两个文件,你可以做这样的事情
<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>
<?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>
<?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>
答案 2 :(得分:0)
JLRishe的答案很棒。我迷路了,直到我知道了。 (我发布了这个问题。)
因为我知道我处理来自SQL Server的数据,所以我将始终拥有对应于两个表的父节点和子节点,并且连接它们的属性是来自两个表的字段:一个独立的表,带有一个主键字段和具有外键字段的从属表。所以我根据我的具体需求概括了解决方案如下:
<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>