如何使用 XPath 和 XSLT 处理一组兄弟节点并基于两个兄弟节点处理特定的兄弟节点子集 [英] 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

查看:33
本文介绍了如何使用 XPath 和 XSLT 处理一组兄弟节点并基于两个兄弟节点处理特定的兄弟节点子集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 

<请求者><person man="四"/><person man="A" id="pers_a"/><perscat category="恢复主管"/><person man="B" id="pers_b"/><perscat category="地面人员"/><要求/><perscat category="按要求分类"/><trade>票据收集器</trade><person man="C" id="pers_c"/><perscat category="地面人员"/><perskill Skill="sk01"/><trade>焊工</trade><esttime>.5小时</esttime><要求/><perscat category="第二个必需的类别"/><esttime>4 天</esttime><person man="D" id="pers_d"/><perscat category="乘坐追逐车辆"/><perskill技能=sk02"/><person man="E"/><perscat category="万事通"/><trade>引擎机械师</trade></reqpers>

需要为每个 person 或 asrequir 元素创建一行,然后用这些元素之间的兄弟姐妹组填充行的单元格.我看过很多例子,包括:如何选择兄弟姐妹

还有这个:如何选择一群兄弟

以及许多其他的.这些都没有处理我的特定问题集,这主要是因为在 XPath 查询中有两个后续兄弟姐妹需要处理.以下是表格外观的示例:

 

<表格><表头><表格行><表格单元格>人物</table-cell><表格单元格>类别/贸易</table-cell><表格单元格>技能等级</table-cell><表格单元格>贸易代码</table-cell><表格单元格>预计时间</table-cell></table-row></表头><桌体><表格行><table-cell>四</table-cell><表格单元格/><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>A</表格单元格><table-cell>恢复主管</table-cell><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>B</表格单元格><table-cell>地面人员</table-cell><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><table-cell>根据需要</table-cell><表格单元格/><表格单元格/><table-cell>帐单收集器</table-cell><表格单元格/></table-row><表格行><表格单元格>C</表格单元格><table-cell>地面人员</table-cell><table-cell>技能被转换为字符串值</table-cell><table-cell>焊工</table-cell><表格单元格>.5小时</表格单元格></table-row><表格行><table-cell>根据需要</table-cell><table-cell>第二个必需的类别</table-cell><表格单元格/><表格单元格/><table-cell>4天</table-cell></table-row><表格行><表格单元格>D</表格单元格><table-cell>乘坐大通车</table-cell><table-cell>技能等级</table-cell><表格单元格/><表格单元格/></table-row><表格行><表格单元格>E</表格单元格><table-cell>Jack of all Trades</table-cell><表格单元格/><table-cell>引擎机械师</table-cell><表格单元格/></table-row></table-body>

同样为了完整性,这是我正在处理的 xml 的架构片段:

<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:序列><xs:element minOccurs="0" ref="applic"/><xs:sequence maxOccurs="unbounded"><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 代码示例,但它仅在有兄弟姐妹的情况下才有效,一旦添加了要求,事情就会分崩离析.我基本上为每个单元格重复此代码,用我期望的元素替换 perscat.

 <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:when test="$perscatSib[count(. | $perscatPrec) = count($perscatPrec)]"><xsl:value-of select="$perscatSib[count(.| $perscatPrec) = count($perscatPrec)]/@category"/></xsl:when><xsl:否则><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:否则></xsl:选择></fo:block></fo:table-cell><继续使用其余的单元格../>

这是我正在制作的表格:

 

<表格><表头><表格行><表格单元格>人物</table-cell><表格单元格>类别/贸易</table-cell><表格单元格>技能等级</table-cell><表格单元格>贸易代码</table-cell><表格单元格>预计时间</table-cell></table-row></表头><桌体><表格行><table-cell>四</table-cell><表格单元格/><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>A</表格单元格><table-cell>恢复主管</table-cell><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>B</表格单元格><table-cell>地面人员</table-cell><表格单元格/>**BillCollector**<表格单元格/></table-row><表格行><table-cell>根据需要</table-cell><表格单元格/><表格单元格/><table-cell>帐单收集器</table-cell><表格单元格/></table-row><表格行><表格单元格>C</表格单元格><table-cell>地面人员</table-cell><table-cell>技能被转换为字符串值</table-cell><table-cell>焊工</table-cell><表格单元格>.5小时</表格单元格></table-row><表格行><table-cell>根据需要</table-cell><table-cell>第二个必需的类别</table-cell><表格单元格/><表格单元格/><table-cell>4天</table-cell></table-row><表格行><表格单元格>D</表格单元格><table-cell>乘坐大通车</table-cell><table-cell>技能等级</table-cell><表格单元格/><表格单元格/></table-row><表格行><表格单元格>E</表格单元格><table-cell>Jack of all Trades</table-cell><表格单元格/><table-cell>引擎机械师</table-cell><表格单元格/></table-row></table-body>

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

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

解决方案

一个键可以帮助将所有元素从 reqpers 分组,这些元素不是 person 或 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: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-cell><表格单元格>类别/贸易</table-cell><表格单元格>技能等级</table-cell><表格单元格>贸易代码</table-cell><表格单元格>预计时间</table-cell></table-row></表头><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::person]/@man"/><xsl:if test="$group[self::asrequir]" >根据需要</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></xsl:模板></xsl:stylesheet>

具有以下输出:

<表头><表格行><表格单元格>人物</table-cell><表格单元格>类别/贸易</table-cell><表格单元格>技能等级</table-cell><表格单元格>贸易代码</table-cell><表格单元格>预计时间</table-cell></table-row></表头><表格行><table-cell>四</table-cell><表格单元格/><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>A</表格单元格><table-cell>恢复主管</table-cell><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><表格单元格>B</表格单元格><table-cell>地面人员</table-cell><表格单元格/><表格单元格/><表格单元格/></table-row><表格行><table-cell>根据需要</table-cell><table-cell>As required Category</table-cell><表格单元格/><table-cell>帐单收集器</table-cell><表格单元格/></table-row><表格行><表格单元格>C</表格单元格><table-cell>地面人员</table-cell><表格单元格>sk01</表格单元格><table-cell>焊工</table-cell><表格单元格>.5小时</表格单元格></table-row><表格行><table-cell>根据需要</table-cell><table-cell>第二个必需的类别</table-cell><表格单元格/><表格单元格/><table-cell>4天</table-cell></table-row><表格行><表格单元格>D</表格单元格><table-cell>乘坐大通车</table-cell><表格单元格>sk02</表格单元格><表格单元格/><表格单元格/></table-row><表格行><表格单元格>E</表格单元格><table-cell>Jack of all Trades</table-cell><表格单元格/><table-cell>引擎机械师</table-cell><表格单元格/></table-row>

I have a group of sibling elements in an xml document that need to be processed with XSLT into a table, actually I'm using Apache FOP to transform it into a pdf. Table rows need to be created whenever one of two types of elements are encountered. The cells in the row consist of the element that causes the row to be created in the first cell and then the following siblings in following cells until the next element that causes a row to be created. Here is some example xml to explain it better:

    <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>

A row needs to be created for each person or asrequir element, then the cells for the row filled with the group of siblings between these elements. I have looked at a lot of examples including: How to select siblings

and this one:How to select group of siblings

as well as many other's. None of these deal with my specific problem set, which is primarily the fact that there are two following-siblings to deal with in the XPath query. Here is an example of what the table should look like:

    <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>

Also for completeness this is the schema snippet for the xml I'm processing:

<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>

Here is an example of the xsl code that is the closest I've come, but it only works if there are person siblings, once the asrequir are added things fall apart. I basically repeat this code for each of the cells, replacing the perscat with the element I'm expecting.

    <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../>

This is the table I'm producing:

    <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>

The table cell with ** around it should not have data in it.. It adds the value from the following asrequir which is not what I want..

I have tried adding following-sibling::person['1']|asrequir['1'] but the asrequir is always true so I get extra siblings in places I don't want them. Any insight or suggestions on how to solve this problem would be very much appreciated. I suspect I need to use a grouping of some sort or a key set, but am not sure how to implement those. While not a newbie to XSLT and XPath, I'm not an expert.

解决方案

A key may help which group all elements from reqpers which are not person or asrequir to the preceding-sibling person or asrequir.

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

The group stats with:

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

The group members are then:

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

Get a value from the group:

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

You may try this:

<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>

With following output:

<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>

这篇关于如何使用 XPath 和 XSLT 处理一组兄弟节点并基于两个兄弟节点处理特定的兄弟节点子集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆