【问题标题】:How to use XPath and XSLT to process a set of sibling nodes and handle a specific subset of siblings based on two of the siblings如何使用 XPath 和 XSLT 处理一组兄弟节点并根据其中两个兄弟节点处理特定的兄弟节点子集
【发布时间】:2016-04-21 19:32:41
【问题描述】:

我在 xml 文档中有一组同级元素需要用 XSLT 处理成表格,实际上我正在使用 Apache FOP 将其转换为 pdf。每当遇到两种类型的元素之一时,都需要创建表行。行中的单元格包括导致在第一个单元格中创建行的元素,然后是后续单元格中的后续兄弟,直到下一个导致创建行的元素。这里有一些示例 xml 可以更好地解释它:

    <pre class="prettyprint"><code class="language-xml">
        <reqpers>
          <person man="Four"/>

          <person man="A" id="pers_a"/>
          <perscat category="Recovery Supervisor"/>

          <person man="B"  id="pers_b"/>
          <perscat category="Ground Personnel"/>

          <asrequir/>
          <perscat category="As Required Category"/>
          <trade>Bill Collector</trade>

          <person man="C"  id="pers_c"/>
          <perscat category="Ground Personnel"/>
          <perskill skill="sk01"/>
          <trade>welder</trade>
          <esttime>.5 hr</esttime>

          <asrequir/>
          <perscat category="2nd Required Category"/>
          <esttime>4 days</esttime>

          <person man="D"  id="pers_d"/>
          <perscat category="Rides in Chase Vehicle"/>
          <perskill skill="sk02"/>

          <person man="E"/>
          <perscat category="Jack of all Trades"/>
          <trade>engine mechanic</trade>
    </reqpers>
   </code>
   </pre>

需要为每个 person 或 asrequir 元素创建一行,然后该行的单元格填充这些元素之间的兄弟姐妹组。 我看过很多例子,包括:How to select siblings

还有这个:How to select group of siblings

以及许多其他的。这些都不涉及我的特定问题集,这主要是因为在 XPath 查询中有两个后续兄弟要处理。 下面是表格应该是什么样子的示例:

    <pre class="prettyprint"><code class="language-xml">
    <table>
        <table-header>
            <table-row>
                <table-cell> Person </table-cell>
                <table-cell> Category/Trade </table-cell>
                <table-cell> Skill level </table-cell>
                <table-cell> Trade code </table-cell>
                <table-cell> Estimated time </table-cell>
            </table-row>
        </table-header>
        <table-body>
            <table-row>
                <table-cell>Four</table-cell>
                <table-cell/>
                <table-cell/>
                <table-cell/>
                <table-cell/>
            </table-row>
            <table-row>
                <table-cell>A</table-cell>
                <table-cell>Recovery Supervisor</table-cell>
                <table-cell/>
                <table-cell/>
                <table-cell/>
            </table-row>
            <table-row>
                <table-cell>B</table-cell>
                <table-cell>Ground Personnel</table-cell>
                <table-cell/>
                <table-cell/>
                <table-cell/>
            </table-row>
            <table-row>
                <table-cell>As required</table-cell>
                <table-cell/>
                <table-cell/>
                <table-cell>Bill Collector</table-cell>
                <table-cell/>
            </table-row>
            <table-row>
                <table-cell>C</table-cell>
                <table-cell>Ground Personnel</table-cell>
                <table-cell>skill gets converted to string value</table-cell>
                <table-cell>welder</table-cell>
                <table-cell>.5 hr</table-cell>
            </table-row>
            <table-row>
                <table-cell>As required</table-cell>
                <table-cell>2nd Required Category</table-cell>
                <table-cell/>
                <table-cell/>
                <table-cell>4 days</table-cell>
            </table-row>
            <table-row>
                <table-cell>D</table-cell>
                <table-cell>Rides in Chase Vehicle</table-cell>
                <table-cell>skill level</table-cell>
                <table-cell/>
                <table-cell/>
            </table-row>
            <table-row>
                <table-cell>E</table-cell>
                <table-cell>Jack of all Trades</table-cell>
                <table-cell/>
                <table-cell>engine mechanic</table-cell>
                <table-cell/>
            </table-row>
        </table-body>
    </table>
</code>
       </pre>

为了完整起见,这是我正在处理的 xml 的架构 sn-p:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
<xs:element name="reqpers" type="reqpersType"/>
<xs:complexType name="reqpersType">
     <xs:sequence>
        <xs:element minOccurs="0" ref="applic"/>
        <xs:sequence maxOccurs="unbounded">
            <xs:choice>
                <xs:element ref="asrequir"/>
                <xs:element ref="person"/>
            </xs:choice>
            <xs:sequence minOccurs="0">
                <xs:element ref="perscat"/>
                <xs:element minOccurs="0" ref="perskill"/>
                <xs:element minOccurs="0" ref="trade"/>
                <xs:element minOccurs="0" ref="esttime"/>
            </xs:sequence>
        </xs:sequence>
    </xs:sequence>
    <xs:attribute ref="refapplic"/>
    <xs:attributeGroup ref="bodyatt"/>
    <xs:attributeGroup ref="cntlcontent"/>
</xs:complexType>

这是我最接近的 xsl 代码示例,但它仅适用于人的兄弟姐妹,一旦添加了 asrequir,事情就会崩溃。我基本上对每个单元格重复此代码,用我期望的元素替换 perscat。

    <xsl:for-each select="person | asrequir">
            <fo:table-row>
                <fo:table-cell text-align="left" padding-before="1mm" padding-after="1mm" padding-left="1mm" padding-right="1mm">
                    <fo:block font-size="10pt">
                        <xsl:value-of select="self::person/@man"/>
                    </fo:block>
                </fo:table-cell>
    <fo:table-cell text-align="left" padding-before="1mm" padding-after="1mm" padding-left="1mm" padding-right="1mm">
        <xsl:variable name="perscatSib" select="following-sibling::perscat"/>
        <xsl:variable name="perscatPrec" select="following-sibling::person[1]/preceding-sibling::perscat"/>
        <fo:block font-size="10pt">
            <xsl:choose>
                <xsl:when test="$perscatSib[count(. | $perscatPrec) = count($perscatPrec)]">
                    <xsl:value-of select="$perscatSib[count(. | $perscatPrec) = count($perscatPrec)]/@category"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:if test="preceding-sibling::person[1] and not(following-sibling::person[1])">
                        <xsl:value-of select="following-sibling::perscat[1]/@category"/>
                    </xsl:if>
                </xsl:otherwise>
            </xsl:choose>
        </fo:block>
    </fo:table-cell>
<continues with the rest of the cells../>

这是我正在制作的表格:

    <pre class="prettyprint"><code class="language-xml">
<table>
    <table-header>
        <table-row>
            <table-cell> Person </table-cell>
            <table-cell> Category/Trade </table-cell>
            <table-cell> Skill level </table-cell>
            <table-cell> Trade code </table-cell>
            <table-cell> Estimated time </table-cell>
        </table-row>
    </table-header>
    <table-body>
        <table-row>
            <table-cell>Four</table-cell>
            <table-cell/>
            <table-cell/>
            <table-cell/>
            <table-cell/>
        </table-row>
        <table-row>
            <table-cell>A</table-cell>
            <table-cell>Recovery Supervisor</table-cell>
            <table-cell/>
            <table-cell/>
            <table-cell/>
        </table-row>
        <table-row>
            <table-cell>B</table-cell>
            <table-cell>Ground Personnel</table-cell>
            <table-cell/>
            **<table-cell>BillCollector</table-cell>**
            <table-cell/>
        </table-row>
        <table-row>
            <table-cell>As required</table-cell>
            <table-cell/>
            <table-cell/>
            <table-cell>Bill Collector</table-cell>
            <table-cell/>
        </table-row>
        <table-row>
            <table-cell>C</table-cell>
            <table-cell>Ground Personnel</table-cell>
            <table-cell>skill gets converted to string value</table-cell>
            <table-cell>welder</table-cell>
            <table-cell>.5 hr</table-cell>
        </table-row>
        <table-row>
            <table-cell>As required</table-cell>
            <table-cell>2nd Required Category</table-cell>
            <table-cell/>
            <table-cell/>
            <table-cell>4 days</table-cell>
        </table-row>
        <table-row>
            <table-cell>D</table-cell>
            <table-cell>Rides in Chase Vehicle</table-cell>
            <table-cell>skill level</table-cell>
            <table-cell/>
            <table-cell/>
        </table-row>
        <table-row>
            <table-cell>E</table-cell>
            <table-cell>Jack of all Trades</table-cell>
            <table-cell/>
            <table-cell>engine mechanic</table-cell>
            <table-cell/>
        </table-row>
    </table-body>
</table>
</code>
       </pre>

带有 ** 的表格单元格中不应包含数据。它添加了以下 asrequir 中的值,这不是我想要的。

我尝试添加 following-sibling::person['1']|asrequir['1'] 但 asrequir 总是正确的,所以我在我不想要的地方获得了额外的兄弟姐妹。任何有关如何解决此问题的见解或建议将不胜感激。我怀疑我需要使用某种分组或键集,但不确定如何实现这些。虽然我不是 XSLT 和 XPath 的新手,但我不是专家。

【问题讨论】:

  • 您可以使用 XSLT 2.0 和 for-each-group group-starting-with 吗?
  • 我现在坚持使用 XSLT 1.0。

标签: xml xslt xpath


【解决方案1】:

一个键可以帮助将 reqpers 中不是 person or asrequir 的所有元素分组到前面的兄弟或 asrequir。

<xsl:key name="kperson" match="reqpers/*[not(self::person or self::asrequir)]" 
       use="generate-id(preceding-sibling::*[self::person or self::asrequir][1])

组统计数据:

<xsl:for-each select="*[self::person or self::asrequir]">

小组成员是:

  <xsl:variable name="this" select="." />
  <xsl:variable name="group" select=". | key('kperson',generate-id($this))" />

从组中获取一个值:

<xsl:value-of select="$group[self::perscat]/@category"/>

你可以试试这个:

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

  <xsl:key name="kperson" match="reqpers/*[not(self::person or self::asrequir)]" 
           use="generate-id(preceding-sibling::*[self::person or self::asrequir][1])"/>
  <xsl:template match="reqpers">
    <table>
    <table-header>
        <table-row>
            <table-cell> Person </table-cell>
            <table-cell> Category/Trade </table-cell>
            <table-cell> Skill level </table-cell>
            <table-cell> Trade code </table-cell>
            <table-cell> Estimated time </table-cell>
        </table-row>
    </table-header>
    <xsl:for-each select="*[self::person or self::asrequir]">

      <xsl:variable name="this" select="." />
      <xsl:variable name="group" select=". | key('kperson',generate-id($this))" />
        <table-row>
            <table-cell>
              <xsl:value-of select="$group[self::person]/@man"/>
              <xsl:if test="$group[self::asrequir]" >As required</xsl:if>
          </table-cell>
            <table-cell><xsl:value-of select="$group[self::perscat]/@category"/></table-cell>
            <table-cell><xsl:value-of select="$group[self::perskill]/@skill"/></table-cell>
            <table-cell><xsl:value-of select="$group[self::trade]"/></table-cell>
            <table-cell><xsl:value-of select="$group[self::esttime]"/></table-cell>
         </table-row>
    </xsl:for-each>
    </table>
  </xsl:template>

</xsl:stylesheet>

输出如下:

<table>
  <table-header>
    <table-row>
      <table-cell> Person </table-cell>
      <table-cell> Category/Trade </table-cell>
      <table-cell> Skill level </table-cell>
      <table-cell> Trade code </table-cell>
      <table-cell> Estimated time </table-cell>
    </table-row>
  </table-header>
  <table-row>
    <table-cell>Four</table-cell>
    <table-cell/>
    <table-cell/>
    <table-cell/>
    <table-cell/>
  </table-row>
  <table-row>
    <table-cell>A</table-cell>
    <table-cell>Recovery Supervisor</table-cell>
    <table-cell/>
    <table-cell/>
    <table-cell/>
  </table-row>
  <table-row>
    <table-cell>B</table-cell>
    <table-cell>Ground Personnel</table-cell>
    <table-cell/>
    <table-cell/>
    <table-cell/>
  </table-row>
  <table-row>
    <table-cell>As required</table-cell>
    <table-cell>As Required Category</table-cell>
    <table-cell/>
    <table-cell>Bill Collector</table-cell>
    <table-cell/>
  </table-row>
  <table-row>
    <table-cell>C</table-cell>
    <table-cell>Ground Personnel</table-cell>
    <table-cell>sk01</table-cell>
    <table-cell>welder</table-cell>
    <table-cell>.5 hr</table-cell>
  </table-row>
  <table-row>
    <table-cell>As required</table-cell>
    <table-cell>2nd Required Category</table-cell>
    <table-cell/>
    <table-cell/>
    <table-cell>4 days</table-cell>
  </table-row>
  <table-row>
    <table-cell>D</table-cell>
    <table-cell>Rides in Chase Vehicle</table-cell>
    <table-cell>sk02</table-cell>
    <table-cell/>
    <table-cell/>
  </table-row>
  <table-row>
    <table-cell>E</table-cell>
    <table-cell>Jack of all Trades</table-cell>
    <table-cell/>
    <table-cell>engine mechanic</table-cell>
    <table-cell/>
  </table-row>
</table>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多