XSLT:根据不同元素下的公共属性值填充表

时间:2018-04-17 11:02:10

标签: xml xslt

我一直在尝试使用基于XML文件中相同属性值的XSLT创建表。我正在努力将基于相同属性的元素链接起来,并循环检索必要数据的XML文件。

我有的XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<LandXML>
  <Survey>
    <GPSSetup id="Setup1" stationName="Ref1">
    </GPSSetup>
    <GPSSetup id="Setup2" stationName="Ref2">
    </GPSSetup>
    <GPSSetup id="Setup3" stationName="Ref3">
    </GPSSetup>
    ...
  </Survey>
  <Survey>
    <GPSSetup id="id1" name="0001">
    </GPSSetup>
    <GPSSetup id="id2" name="0002">
    </GPSSetup>
    <GPSSetup id="id3" name="0003">
    </GPSSetup>
    ...
    <GPSVector setupID_a="Ref1">
      <TargetPoint pntRef="0001"></TargetPoint>
    </GPSVector>
    <GPSVector setupID_a="Ref1">
      <TargetPoint pntRef="0002"></TargetPoint>
    </GPSVector>
    <GPSVector setupID_a="Ref2">
      <TargetPoint pntRef="0003"></TargetPoint>
    </GPSVector>
    ...
  </Survey>
  <HexagonLandXML>
    <Point uniqueID="0001">
      <Coordinates n="1234"></Coordinates>
    </Point>
    <Point uniqueID="name2">
      <Coordinates n="1225"></Coordinates>
    </Point>
    <Point uniqueID="name3">
      <Coordinates n="1335"></Coordinates>
    </Point>
  </HexagonLandXML>
</LandXML>

我想在每个&#34; stationName&#34;下输出一个html文件。在XML中找到的值,创建一个表,其中对应的&#34; pntRef&#34;和&#34; n&#34;值显示。

我希望实现的输出:

Ref1:
0001   1234
0002   1225

Ref2:
0003   1335

我到目前为止所做的XSLT文件:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
            xmlns:l="http://www.landxml.org/schema/LandXML-1.2"
            xmlns:h="http://xml.hexagon.com/schema/HeXML-1.7"
            xmlns:msxsl="urn:schemas-microsoft-com:xslt"
            exclude-result-prefixes="h l"
            extension-element-prefixes="h l msxsl">
<xsl:output method="html" indent="no" encoding="utf-8"/> 
<xsl:variable name="fileExt" select="'html'"/>

<xsl:template match="/">
  <html>
    <head>
      <style>
      </style>
    </head>
    <body>
      <xsl:apply-templates select="//l:LandXML/l:Survey[1]/l:GPSSetup">
      </xsl:apply-templates>
    </body>
  </html>
</xsl:template>

<xsl:key name="keyLGPSVector" match="l:GPSVector" use="@setupID_A"/>
<xsl:key name="keyH" match="h:Point" use="@uniqueID"/>

<xsl:template match="//l:LandXML/l:Survey[1]/l:GPSSetup">
  <xsl:variable name="Survey1L" select="."/>
  <xsl:variable name="Survey1LID" select="key('keyLGPSVector', $Survey1L/@id)"/>
  <xsl:variable name="PointH" select="key('keyH', $Survey1L/@id)"/>

  <xsl:for-each select=".">
    <h5>
    <xsl:value-of select="@stationName"/>
    </h5>

    <table>
        <thead>
            <tr>
                <th>Number</th>
                <th>n [m]</th>
            </tr>
        </thead>
        <tbody>
            <xsl:for-each select="$Survey1LID">
                <xsl:if test="$Survey1LID/@setupID_A = $Survey1L/@id">
                <tr>
                    <td><xsl:value-of select="current()/l:TargetPoint/@pntRef"/></td>
                    <td><xsl:value-of select="$PointH/h:Coordinates/@n"/></td>
                </tr>
                </xsl:if>
            </xsl:for-each>
        </tbody>
    </table>
</xsl:for-each>

有了这个,我就可以实现正确的&#34; pntRef&#34;值显示在每个&#34; Ref()&#34;标题,但我不能得到&#34; n&#34;值。从阅读这个论坛,我明白for-each可能不是在这种情况下使用的最佳元素。

很抱歉,如果编辑错误或不透明,这是我的第一篇文章。

2 个答案:

答案 0 :(得分:1)

您不需要<xsl:for-each select=".">,您可以将其删除为模板内部,但无论如何只有一个上下文节点。

然后我认为您应该将<xsl:variable name="PointH" select="key('keyH', $Survey1L/@id)"/>移到第二个for-each内,而不是<xsl:variable name="PointH" select="key('keyH', TargetPoint/@pntRef)"/>

如果您仍然遇到问题,请考虑将XML和XSLT示例编辑为最小但完整且一致的,目前您的XML示例并未显示任何名称空间,但您的XSLT至少假定为两个。

答案 1 :(得分:1)

我认为这就是你所需要的;请参阅解释说明:XSLT Fiddle

代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array"
    version="3.0">

    <!-- create the html body -->
    <xsl:template match="/">
        <html>
            <head>
                <style></style>
            </head>
            <body>  
                 <xsl:apply-templates select="/LandXML/Survey/GPSSetup[./@stationName]" />
            </body>  
        </html>
    </xsl:template>

    <!-- create an entry per station -->
    <xsl:template match="/LandXML/Survey/GPSSetup">
        <xsl:variable name="stationName" select="./@stationName" />
        <h5>
            <xsl:value-of select="$stationName"/>
        </h5>
        <table>
            <thead>
                <tr>
                    <th>Number</th>
                    <th>n [m]</th>
                </tr>
            </thead>
            <tbody>
                 <xsl:apply-templates select="../../Survey/GPSVector[@setupID_a = $stationName]/TargetPoint" />
            </tbody>
        </table>
    </xsl:template>

    <!-- find all target points related to the station -->
    <xsl:template match="/LandXML/Survey/GPSVector/TargetPoint">
        <xsl:variable name="pntRef" select="./@pntRef" />
         <xsl:apply-templates select="../../../HexagonLandXML/Point[@uniqueID = $pntRef]/Coordinates" />
    </xsl:template>

    <!-- create a row for each target point / coordinate pair under the station -->
    <xsl:template match="/LandXML/HexagonLandXML/Point/Coordinates">
        <tr>
            <td><xsl:value-of select="../@uniqueID" /></td>
            <td><xsl:value-of select="./@n" /></td>
        </tr>
    </xsl:template>

</xsl:stylesheet>

解释

  • 模板<xsl:template match="/LandXML/HexagonLandXML/Point/Coordinates">会创建包含uniqueIDn值的行;但是不知道应该放在哪里。
  • 代码<xsl:apply-templates select="../../../HexagonLandXML/Point[@uniqueID = $pntRef]/Coordinates" />从上面的模板中撤回这些结果,其中uniqueID值与当前模板的pntRef值匹配;所以我们只显示每个pntRef的相关行。注意:我在我的选择中使用了相对XPath(../../../HexagonLandXML/Point[@uniqueID = $pntRef]/Coordinates;但同样可以使用绝对值(/LandXML/HexagonLandXML/Point[@uniqueID = $pntRef]/Coordinates)。相对的一个在当前模板的匹配上下文中起作用,而绝对一个起作用来自源文档的根元素。后者可能更容易理解;但如果从XML的结构中推断出任何关系,前者可能会有用(尽管我认为这不是这种情况)。
  • 在找到与其他模板的工作站相关的TargetPoints时,我们重复上述使用apply-templates过滤另一个模板匹配的技巧。
  • 对于每站创建一个条目的模板,我们不需要对值进行过滤;我们只是在发生所有匹配时提取它们。