XML - XSLT List of elements - Joining elements wit

2019-08-19 05:59发布

I'm trying to produce a XML file with a certain structure but I'm stuck,

This is my input XML file:

    <report>
    <page name="Simple MasterPage">
        <table id="__bookmark_1">
            <table-band band-type="BAND_HEADER">
                <row>
                    <cell>
                        <label>PERSONNAME</label>
                    </cell>
                    <cell>
                        <label>PERSONID</label>
                    </cell>
                    <cell>
                        <label>NUMELEM</label>
                    </cell>
                    <cell>
                        <label>CREATIONDATE</label>
                    </cell>
<cell>
                        <label>CREATIONDATE</label>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>John</data>
                    </cell>
                    <cell>
                        <data>p1</data>
                    </cell>
                    <cell>
                        <data>1</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>John</data>
                    </cell>
                    <cell>
                        <data>p1</data>
                    </cell>
                    <cell>
                        <data>2</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>John</data>
                    </cell>
                    <cell>
                        <data>p1</data>
                    </cell>
                    <cell>
                        <data>3</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>John</data>
                    </cell>
                    <cell>
                        <data>p1</data>
                    </cell>
                    <cell>
                        <data>4</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>John</data>
                    </cell>
                    <cell>
                        <data>p1</data>
                    </cell>
                    <cell>
                        <data>5</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>Marie</data>
                    </cell>
                    <cell>
                        <data>p2</data>
                    </cell>
                    <cell>
                        <data>6</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
            <table-band band-type="BAND_DETAIL">
                <row>
                    <cell>
                        <data>Marie</data>
                    </cell>
                    <cell>
                        <data>p2</data>
                    </cell>
                    <cell>
                        <data>7</data>
                    </cell>
                    <cell>
                        <data>2018-06-21</data>
                    </cell>
                </row>
            </table-band>
        </table>
    </page>
</report>

This is the XSLT I'm using to transform it, that user @Parfait teached me how to build on a very similar post:

    <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <xsl:output method="xml" omit-xml-declaration="no" encoding="utf-8"/>
    <xsl:strip-space elements="*"/>


    <xsl:template match="/report/page/table">
     <entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" versao="1.0">
            <!--Para comecar da 2 tabela-->
            <xsl:apply-templates select="table-band[position() &gt; 1] "/>
     </entry> 
    </xsl:template>

    <xsl:template name="top_build-attrib">
      <xsl:param name="label_val"/>
      <xsl:param name="attrib_nm"/>
       <xsl:variable name="row_num" select="count(table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
       <xsl:attribute name="{$attrib_nm}"><xsl:value-of select="table-band[2]/row/cell[position()=$row_num]/data"/></xsl:attribute>
    </xsl:template>

    <xsl:template name="build-attrib">
      <xsl:param name="label_val"/>
      <xsl:param name="attrib_nm"/>
       <xsl:variable name="row_num" select="count(ancestor::table/table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
       <xsl:attribute name="{$attrib_nm}"><xsl:value-of select="row/cell[position()=$row_num]/data"/></xsl:attribute>
    </xsl:template>


    <xsl:template name="elem_value">
      <xsl:param name="label_val"/>
       <xsl:variable name="row_num" select="count(ancestor::table/table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
       <xsl:value-of select="row/cell[position()=$row_num]/data"/>
    </xsl:template>

    <xsl:template match="table-band">

            <person>
                <xsl:call-template name="build-attrib">
                   <xsl:with-param name="label_val">PERSONNAME</xsl:with-param>
                   <xsl:with-param name="attrib_nm">personName</xsl:with-param>
                </xsl:call-template>

                <xsl:call-template name="build-attrib">
                   <xsl:with-param name="label_val">PERSONID</xsl:with-param>
                   <xsl:with-param name="attrib_nm">personID</xsl:with-param>
                </xsl:call-template>

                <listOfElements>
                    <element>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">NUMELEM</xsl:with-param>
                            <xsl:with-param name="attrib_nm">numElem</xsl:with-param>
                        </xsl:call-template>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">CREATIONDATE</xsl:with-param>
                            <xsl:with-param name="attrib_nm">creationDate</xsl:with-param>
                        </xsl:call-template>
                   </element>
                </listOfElements>
            </person> 
    </xsl:template>

    </xsl:stylesheet>

And this this the result I get:

    <?xml version="1.0" encoding="UTF-8"?>
      <entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" versao="1.0">
        <person personID="p1" personName="John">
         <listOfElements>
            <element creationDate="2018-06-21" numElem="1"/>
         </listOfElements>
        </person>
        <person personID="p1" personName="John">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="2"/>
          </listOfElements>
        </person>
        <person personID="p1" personName="John">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="3"/>
          </listOfElements>
        </person>
        <person personID="p1" personName="John">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="4"/>
          </listOfElements>
        </person>
        <person personID="p1" personName="John">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="5"/>
          </listOfElements>
        </person>
        <person personID="p2" personName="Marie">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="6"/>
          </listOfElements>
        </person>
        <person personID="p2" personName="Marie">
          <listOfElements>
            <element creationDate="2018-06-21" numElem="7"/>
          </listOfElements>
        </person>
     </entry>

But the result I'm looking for and that am required to prodocue is this one:

    <?xml version="1.0" encoding="utf-8"?>
     <entry>
       <person personID="p1" personName="John">
        <listOfElements>
            <element creationDate="2018-06-21" numElem="1" />
            <element creationDate="2018-06-21" numElem="2" />
            <element creationDate="2018-06-21" numElem="3" />
            <element creationDate="2018-06-21" numElem="4" />
            <element creationDate="2018-06-21" numElem="5"/>
        </listOfElements>
       </person>
       <person personID="p2" personName="Marie">
        <listOfElements>
            <element creationDate="2018-06-21" numElem="6" />
            <element creationDate="2018-06-21" numElem="7" />
        </listOfElements>
       </person>
    </entry>

Meaning that the I'm not being able to produce the listOfElements like I should, I'm printing a new person node instead of "joining" the element nodes in the same person node (inside the listOfElements node)

I know that the solution must have something to do with comparing the presonID or personName attributes, but I'm just not getting there...

I really need some help guys, thank you!

EDIT

I tried addin the xsl:for-each-group before each element tag but I still get the same result. Did the following:

               <listOfElements>
                    <xsl:for-each-group select="table-band[@band-type='BAND_DETAIL']/row" group-by="cell[2]/data">
                    <element>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">NUMELEM</xsl:with-param>
                            <xsl:with-param name="attrib_nm">numElem</xsl:with-param>
                        </xsl:call-template>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">CREATIONDATE</xsl:with-param>
                            <xsl:with-param name="attrib_nm">creationDate</xsl:with-param>
                        </xsl:call-template>
                   </element>
                   </xsl:for-each-group>
                </listOfElements>

EDIT I changed the input xml and output, meaning that the input xml lost the NUMDOC and CREATIONDATE elements, and gained a new one, ELEMENTTYPE,

So, I lost the grouping factor I used in the inner for-each-group, that was NUMDOC, like @Parfait teached me. Now I don't know how to group the elements with only having the ELEMENTTYPE. Note that I want to be able to have, for the same person, different elements with the same ELEMENTTYPE, so this can't be use for the factor in the for-each-group, or can it?

INPUT XML:(UPDATED)

    <report>
        <page name="Simple MasterPage">
            <table id="__bookmark_1">
                <table-band band-type="BAND_HEADER">
                    <row>
                        <cell>
                            <label>PERSONNAME</label>
                        </cell>
                        <cell>
                            <label>PERSONID</label>
                        </cell>
                        <cell>
                            <label>ELEMENTTYPE</label>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>John</data>
                        </cell>
                        <cell>
                            <data>p1</data>
                        </cell>
                        <cell>
                            <data>001</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>John</data>
                        </cell>
                        <cell>
                            <data>p1</data>
                        </cell>
                        <cell>
                            <data>001</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>John</data>
                        </cell>
                        <cell>
                            <data>p1</data>
                        </cell>
                        <cell>
                            <data>002</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>John</data>
                        </cell>
                        <cell>
                            <data>p1</data>
                        </cell>
                        <cell>
                            <data>001</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>John</data>
                        </cell>
                        <cell>
                            <data>p1</data>
                        </cell>
                        <cell>
                            <data>002</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>Marie</data>
                        </cell>
                        <cell>
                            <data>p2</data>
                        </cell>
                        <cell>
                            <data>001</data>
                        </cell>
                    </row>
                </table-band>
                <table-band band-type="BAND_DETAIL">
                    <row>
                        <cell>
                            <data>Marie</data>
                        </cell>
                        <cell>
                            <data>p2</data>
                        </cell>
                        <cell>
                            <data>001</data>
                        </cell>
                    </row>
                </table-band>
            </table>
        </page>
    </report>

So the output I wanna get is:

    <?xml version="1.0" encoding="utf-8"?>
         <entry>
           <person personID="p1" personName="John">
            <listOfElements>
                <element elementType="001" />
                <element elementType="001" />
                <element elementType="002" />
                <element elementType="001" />
                <element elementType="002" />
            </listOfElements>
           </person>
           <person personID="p2" personName="Marie">
            <listOfElements>
                <element elementType="001" />
                <element elementType="001" />
            </listOfElements>
           </person>
        </entry>

I don't know what to use in the inner for-each-group, before , like @Parfait showed me, I used NUMDOC, but now I don't have anything to distinguish between docs!

Thanks!

Alexandre Jacinto

2条回答
叛逆
2楼-- · 2019-08-19 06:39

Consider applyling a two-layered <xsl:for-each-group> in your last template of current XSLT, pointing to the PERSONNAME and NUMELEM corresponding position in first <table-band>:

XSLT Fiddle Demo

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="utf-8"/>
   <xsl:strip-space elements="*"/>

   <xsl:template match="/report/page">
      <entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" versao="1.0">
        <!--Para comecar da 2 tabela-->
        <xsl:apply-templates select="table"/>
      </entry> 
   </xsl:template>

   <xsl:template name="top_build-attrib">
     <xsl:param name="label_val"/>
     <xsl:param name="attrib_nm"/>
      <xsl:variable name="row_num" select="count(table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
      <xsl:attribute name="{$attrib_nm}"><xsl:value-of select="table-band[2]/row/cell[position()=$row_num]/data"/></xsl:attribute>
   </xsl:template>

   <xsl:template name="build-attrib">
     <xsl:param name="label_val"/>
     <xsl:param name="attrib_nm"/>
      <xsl:variable name="row_num" select="count(ancestor::table/table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
      <xsl:attribute name="{$attrib_nm}"><xsl:value-of select="row/cell[position()=$row_num]/data"/></xsl:attribute>
   </xsl:template>

   <xsl:template name="elem_value">
     <xsl:param name="label_val"/>
      <xsl:variable name="row_num" select="count(ancestor::table/table-band[1]/row/cell[label=$label_val]/preceding-sibling::*)+1"/>
      <xsl:value-of select="row/cell[position()=$row_num]/data"/>
   </xsl:template>

   <xsl:template match="table">

       <xsl:for-each-group select="table-band" group-by="row/cell[position()=
                      count(ancestor::table/table-band[1]/row/cell[label='PERSONNAME']/preceding-sibling::*)+1]/data">
         <person>
            <xsl:call-template name="build-attrib">
               <xsl:with-param name="label_val">PERSONID</xsl:with-param>
               <xsl:with-param name="attrib_nm">personID</xsl:with-param>
            </xsl:call-template>

            <xsl:call-template name="build-attrib">
               <xsl:with-param name="label_val">PERSONNAME</xsl:with-param>
               <xsl:with-param name="attrib_nm">personName</xsl:with-param>
            </xsl:call-template>

            <listOfElements>
                <xsl:for-each-group select="current-group()" group-by="row/cell[position()=
                      count(ancestor::table/table-band[1]/row/cell[label='NUMELEM']/preceding-sibling::*)+1]">
                    <element>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">NUMELEM</xsl:with-param>
                            <xsl:with-param name="attrib_nm">numElem</xsl:with-param>
                        </xsl:call-template>
                        <xsl:call-template name="build-attrib">
                            <xsl:with-param name="label_val">CREATIONDATE</xsl:with-param>
                            <xsl:with-param name="attrib_nm">creationDate</xsl:with-param>
                        </xsl:call-template>
                   </element>
                </xsl:for-each-group>
            </listOfElements>
        </person> 

       </xsl:for-each-group>
   </xsl:template>

</xsl:stylesheet>
查看更多
Deceive 欺骗
3楼-- · 2019-08-19 06:41

Here is a minimal stylesheet incorporating my suggestion made in a comment (with the grouping key adjusted to take the data value from the second cell):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/report/page/table">
   <entry versao="1.0">
        <xsl:for-each-group select="table-band[@band-type='BAND_DETAIL']/row" group-by="cell[2]/data">
            <person personID="{current-grouping-key()}" personName="{cell[1]/data}">
                <listOfElements>
                    <xsl:apply-templates select="current-group()"/>
                </listOfElements>
            </person> 
       </xsl:for-each-group>
    </entry> 
</xsl:template>

<xsl:template match="row">
    <element creationDate="{cell[4]/data}" numElem="{cell[3]/data}"/>
</xsl:template>

</xsl:stylesheet>

At https://xsltfiddle.liberty-development.net/bdxtq7 where Saxon 9.8 HE is used the result for your sample is

<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" versao="1.0">
   <person personID="p1" personName="John">
      <listOfElements>
         <element creationDate="2018-06-21" numElem="1"/>
         <element creationDate="2018-06-21" numElem="2"/>
         <element creationDate="2018-06-21" numElem="3"/>
         <element creationDate="2018-06-21" numElem="4"/>
         <element creationDate="2018-06-21" numElem="5"/>
      </listOfElements>
   </person>
   <person personID="p2" personName="Marie">
      <listOfElements>
         <element creationDate="2018-06-21" numElem="6"/>
         <element creationDate="2018-06-21" numElem="7"/>
      </listOfElements>
   </person>
</entry>
查看更多
登录 后发表回答