如何使用XSLT 1.0填写XML列表中的漏洞?

时间:2017-02-14 17:09:52

标签: xml xslt xslt-1.0

我有一个要在HTML表格中显示的项目列表。这个清单有"漏洞"但是,需要填写它才能正确显示(xy是表格中的列和行)。我无法弄清楚在哪里开始这样做。

<items>
  <row y="1">
    <item x="1" y="1" data="importantStuff1"/>
  </row>
  <row y="2">
    <item x="2" y="2" data="importantStuff3"/>
  </row>
  <row y="3">
    <item x="5" y="3" data="importantStuff1"/>
  </row>
  <row y="4">
    <item x="3" y="4" data="importantStuff2"/>
    <item x="4" y="4" data="importantStuff3"/>
  </row>
</items>

我需要的是以下内容:

<items>
  <row y="1">
    <item x="1" y="1" data="importantStuff1"/>
    <item x="2" y="1" data="padding"/>
    <item x="3" y="1" data="padding"/>
    <item x="4" y="1" data="padding"/>
    <item x="5" y="1" data="padding"/>
  </row>
  <row y="2">
    <item x="1" y="2" data="padding"/>
    <item x="2" y="2" data="importantStuff3"/>
    <item x="3" y="2" data="padding"/>
    <item x="4" y="2" data="padding"/>
    <item x="5" y="2" data="padding"/>
  </row>
  <row y="3">
    <item x="1" y="3" data="padding"/>
    <item x="2" y="3" data="padding"/>
    <item x="3" y="3" data="padding"/>
    <item x="4" y="3" data="padding"/>
    <item x="5" y="3" data="importantStuff1"/>
  </row>
  <row y="4">
    <item x="1" y="4" data="padding"/>
    <item x="2" y="4" data="padding"/>
    <item x="3" y="4" data="importantStuff2"/>
    <item x="4" y="4" data="importantStuff3"/>
    <item x="5" y="4" data="padding"/>
  </row>
</items>

如何填充列表看起来像这样?保证订购物品,我知道每个轴上有多少物品。

编辑:我没有意识到问题可能被解释为我想要一个简单的列表。每个项目中都有其他数据,这使得保留现有项目节点变得非常重要。所以我需要的是一种只创建填充节点并将现有节点保持原样的方法。

为了创建一个演示我的问题的最小例子,我有点过分了。对不起。

3 个答案:

答案 0 :(得分:2)

如果您的示例与实际数据一样简单,您应该能够使用递归模板调用来检查5个项目中的每一个。如果它在那里,应用模板。如果不是,请创建它。

示例...

XML输入

<items>
    <row y="1">
        <item x="1" y="1" data="importantStuff1"/>
    </row>
    <row y="2">
        <item x="2" y="2" data="importantStuff3"/>
    </row>
    <row y="3">
        <item x="5" y="3" data="importantStuff1"/>
    </row>
    <row y="4">
        <item x="3" y="4" data="importantStuff2"/>
        <item x="4" y="4" data="importantStuff3"/>
    </row>5
</items>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="items" select="5"/>

  <!--Identity transform. Copy everything as-is unless overridden by
  a more specific template.--> 
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="row">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:call-template name="outputItems">
        <xsl:with-param name="count" select="1"/>
      </xsl:call-template>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="outputItems">
    <xsl:param name="count"/>
    <xsl:choose>
      <!--Item already exists.-->
      <xsl:when test="item[@x=$count]">
        <xsl:apply-templates select="item[@x=$count]"/>
      </xsl:when>
      <!--Item does not exist. create it.-->
      <xsl:otherwise>
        <item x="{$count}" y="{@y}" data="padding"/>
      </xsl:otherwise>
    </xsl:choose>
    <!--Call this template again if needed.-->
    <xsl:if test="$items > $count">
      <xsl:call-template name="outputItems">
        <xsl:with-param name="count" select="$count + 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

<强>输出

<items>
   <row y="1">
      <item x="1" y="1" data="importantStuff1"/>
      <item x="2" y="1" data="padding"/>
      <item x="3" y="1" data="padding"/>
      <item x="4" y="1" data="padding"/>
      <item x="5" y="1" data="padding"/>
   </row>
   <row y="2">
      <item x="1" y="2" data="padding"/>
      <item x="2" y="2" data="importantStuff3"/>
      <item x="3" y="2" data="padding"/>
      <item x="4" y="2" data="padding"/>
      <item x="5" y="2" data="padding"/>
   </row>
   <row y="3">
      <item x="1" y="3" data="padding"/>
      <item x="2" y="3" data="padding"/>
      <item x="3" y="3" data="padding"/>
      <item x="4" y="3" data="padding"/>
      <item x="5" y="3" data="importantStuff1"/>
   </row>
   <row y="4">
      <item x="1" y="4" data="padding"/>
      <item x="2" y="4" data="padding"/>
      <item x="3" y="4" data="importantStuff2"/>
      <item x="4" y="4" data="importantStuff3"/>
      <item x="5" y="4" data="padding"/>
   </row>5
</items>

答案 1 :(得分:1)

  

我知道每个轴上有多少项。

假设您可以将这些知识作为参数传递给样式表,我建议您这样做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:param name="rows" select="4"/>
<xsl:param name="cols" select="5"/>

<xsl:key name="item" match="item" use="concat(@x, '|', @y)" />

<xsl:template match="/items">
    <xsl:copy>
        <xsl:call-template name="generate-rows"/>
    </xsl:copy>
</xsl:template>

<xsl:template name="generate-rows">
    <xsl:param name="y" select="1"/>
    <xsl:if test="$y &lt;= $rows">
        <row y="{$y}">
            <xsl:call-template name="generate-cols">
                <xsl:with-param name="y" select="$y"/>
            </xsl:call-template>
        </row>
        <!-- recursive call -->
        <xsl:call-template name="generate-rows">
            <xsl:with-param name="y" select="$y + 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template name="generate-cols">
    <xsl:param name="y"/>
    <xsl:param name="x" select="1"/>
    <xsl:if test="$x &lt;= $cols">
         <item x="{$x}" y="{$y}" >
            <xsl:variable name="exisiting-item" select="key('item', concat($x, '|', $y))" />
            <xsl:attribute name="data">
                <xsl:choose>
                    <xsl:when test="$exisiting-item">
                        <xsl:value-of select="$exisiting-item/@data"/>
                    </xsl:when>
                    <xsl:otherwise>padding</xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
        </item>
        <!-- recursive call -->
        <xsl:call-template name="generate-cols">
            <xsl:with-param name="y" select="$y"/>
            <xsl:with-param name="x" select="$x + 1"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

这将预先生成所提供尺寸的表格,并使用相应项目中的数据填充每个单元格(如果存在)。

-

加了:

如果需要从输入中推导出表格尺寸,请更改:

<xsl:param name="rows" select="4"/>
<xsl:param name="cols" select="5"/>

为:

<xsl:variable name="rows" select="/items/row[last()]/@y"/>
<xsl:variable name="cols">
    <xsl:for-each select="/items/row/item">
        <xsl:sort select="@x" data-type="number" order="ascending"/>
        <xsl:if test="position()=last()">
            <xsl:value-of select="@x"/>
        </xsl:if>
    </xsl:for-each>
</xsl:variable>

这会将最后一行的y值和任何项的最大x值作为表格尺寸。

答案 2 :(得分:0)

您可以使用以下XSLT实现此目的。这是一种特殊的解决方案,但是通过使用命名空间的XML岛创建'count'变量是一种非常通用的方法,用于模拟具有固定计数的'for'循环。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cnt="http://cnt.com" exclude-result-prefixes="cnt">
  <xsl:output method="xml" indent="yes" />

  <!-- creating a "count" XML structure -->
  <cnt:count>
    <x cnt="1" />
    <x cnt="2" />
    <x cnt="3" />
    <x cnt="4" />
    <x cnt="5" />
  </cnt:count>

  <xsl:template match="items">
    <items>
      <xsl:for-each select="row">
        <xsl:variable name="varY" select="@y" />   <!-- saving the value of '@y' -->
        <row y="{$varY}">
          <xsl:for-each select="document('')/xsl:stylesheet/cnt:count/x">
            <item x="{@cnt}" y="{$varY}" />
          </xsl:for-each>      
        </row>
      </xsl:for-each>      
    </items>
  </xsl:template>

</xsl:stylesheet>

输出符合要求。