如何基于2个条件重构XML?

时间:2017-01-10 13:47:29

标签: xml xslt xpath xquery

我有一个具有以下结构的XML:

<building>
    <employee>
        <zone>1</zone>
        <system>A</system>
        <name>Jhon</name>
    </employee>
    <employee>
        <zone>1</zone>
        <system>A</system>
        <name>Paul</name>
    </employee>
    <employee>
        <zone>1</zone>
        <system>B</system>
        <name>Matt</name>
    </employee>
    <employee>
        <zone>2</zone>
        <system>A</system>
        <name>Bob</name>
    </employee>
    <employee>
        <zone>2</zone>
        <system>A</system>
        <name>Peter</name>
    </employee>
</building>

员工按区域和系统升序排序。

如何使用以下格式重构XML?:

<building>
    <block>
        <employee>
            <zone>1</zone>
            <system>A</system>
            <name>Jhon</name>
        </employee>
        <employee>
            <zone>1</zone>
            <system>A</system>
            <name>Paul</name>
        </employee>
    </block>
    <block>
        <employee>
            <zone>1</zone>
            <system>B</system>
            <name>Matt</name>
        </employee>
    </block>
    <block>
        <employee>
            <zone>2</zone>
            <system>A</system>
            <name>Bob</name>
        </employee>
        <employee>
            <zone>2</zone>
            <system>A</system>
            <name>Peter</name>
        </employee>
    </block>
</building>

分组条件是按区域和系统划分的(对于那些共享同一区域和同一系统的员工)。

我尝试使用XSLT和XQuery没有任何成功。

1 个答案:

答案 0 :(得分:3)

当您需要对&#34; 2条件&#34;进行分组时,您可以组合这些条件来创建复合键。

使用分隔符是个好主意,这样您就可以获得准确的密钥。可以使用concat()(XPath 1.0或2.0)或string-join()(仅限XPath 2.0)进行组合。

以下是一些例子。您会注意到在所有3个中我基本上使用相同的东西来创建分组键:concat(zone,'|',system)

XSLT 1.0(或2.0)(Muenchian分组)

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

  <xsl:key name="employeeByBlock" match="employee" use="concat(zone,'|',system)"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:for-each select="employee[count(.|key('employeeByBlock',concat(zone,'|',system))[1])=1]">
        <xsl:sort select="zone"/>
        <xsl:sort select="system" data-type="text"/>
        <block>
          <xsl:apply-templates select="key('employeeByBlock',concat(zone,'|',system))">
            <xsl:sort select="name" data-type="text"/>
          </xsl:apply-templates>
        </block>
      </xsl:for-each>      
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XSLT 2.0 xsl:for-each-group

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

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:for-each-group select="employee" group-by="concat(zone,'|',system)">
        <xsl:sort select="zone"/>
        <xsl:sort select="system" data-type="text"/>        
        <block>
          <xsl:apply-templates select="current-group()">
            <xsl:sort select="name" data-type="text"/>
          </xsl:apply-templates>
        </block>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XQuery 3.0 group by

xquery version "3.0";
<building>{
for $employee in /building/employee
let $zone := $employee/zone
let $system := $employee/system
group by $zone, $system 
order by $zone, $system
return
    <block>{
    for $x in $employee
    order by $x/name
    return $x
    }</block>
}</building>

这三个例子都产生相同的输出*:

<building>
   <block>
      <employee>
         <zone>1</zone>
         <system>A</system>
         <name>Jhon</name>
      </employee>
      <employee>
         <zone>1</zone>
         <system>A</system>
         <name>Paul</name>
      </employee>
   </block>
   <block>
      <employee>
         <zone>1</zone>
         <system>B</system>
         <name>Matt</name>
      </employee>
   </block>
   <block>
      <employee>
         <zone>2</zone>
         <system>A</system>
         <name>Bob</name>
      </employee>
      <employee>
         <zone>2</zone>
         <system>A</system>
         <name>Peter</name>
      </employee>
   </block>
</building>

*使用Saxon 6.5.5测试XSLT 1.0。使用Saxon-HE 9.5测试XSLT 2.0和XQuery。