goshanoob
goshanoob

Reputation: 75

Wrapping of node with different attribute in XSLT 2.0

I have input XML-document like this or different:

<X3D version="3.2">
   <Scene>
      <Background skyColor="1 1 1"/>
      <Viewpoint position="0 0 4"/>
      <Transform DEF="body_0">
         <Inline url="body_0.x3d"/>
         <Transform DEF="body_1" translation="0,0.2,0">
            <Inline url="body_1.x3d"/>
            <Transform DEF="body_2" translation="0,0,0">
               <Inline url="body_2.x3d"/>
               <Transform DEF="body_3" translation="0,0,0">
                  <Inline url="body_3.x3d"/>
                  <Transform DEF="body_4" translation="0.5,0,0">
                     <Inline url="body_4.x3d"/>
                     <Transform DEF="body_5" translation="0,0,0">
                        <Inline url="body_5.x3d"/>
                     </Transform>
                  </Transform>
               </Transform>
            </Transform>
            <Transform DEF="body_6" translation="0,0,0">
               <Inline url="body_6.x3d"/>
               <Transform DEF="body_7" translation="0,0,0">
                  <Inline url="body_7.x3d"/>
                  <Transform DEF="body_8" translation="0.5,0,0">
                     <Inline url="body_8.x3d"/>
                     <Transform DEF="body_9" translation="0,0,0">
                        <Inline url="body_9.x3d"/>
                     </Transform>
                  </Transform>
               </Transform>
            </Transform>
         </Transform>
      </Transform>
   </Scene>
</X3D>

I need to wrap node with certain attribure DEF to one more node . For example, attribute calculates in variable $find. So I have xslt-stylesheet:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  
  <xsl:template match="/">
      <xsl:variable name="find" select="concat('body_','3')"/>
      <xsl:for-each-group select="//Transform" 
        group-adjacent="@DEF = $find">
        <xsl:choose>
          <xsl:when test="current-grouping-key()">
            <Transform rotation="1 0 0 1.57">
              <xsl:sequence select="current-group()"/>
            </Transform>
          </xsl:when>
          <xsl:otherwise>
            <xsl:sequence select="current-group()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    
  </xsl:template>
</xsl:stylesheet>

I hope to see output document whith next code:

<X3D version="3.2">
   <Scene>
      <Background skyColor="1 1 1"/>
      <Viewpoint position="0 0 4"/>
      <Transform DEF="body_0">
         <Inline url="body_0.x3d"/>
         <Transform DEF="body_1" translation="0,0.2,0">
            <Inline url="body_1.x3d"/>
            <Transform DEF="body_2" translation="0,0,0">
               <Inline url="body_2.x3d"/>
               
            <Transform  rotation="1 0 0 1.57">
               <Transform DEF="body_3" translation="0,0,0">
                  <Inline url="body_3.x3d"/>
                  <Transform DEF="body_4" translation="0.5,0,0">
                     <Inline url="body_4.x3d"/>
                     <Transform DEF="body_5" translation="0,0,0">
                        <Inline url="body_5.x3d"/>
                     </Transform>
                  </Transform>
               </Transform>
            </Transform> 
               
            </Transform>
            <Transform DEF="body_6" translation="0,0,0">
               <Inline url="body_6.x3d"/>
               <Transform DEF="body_7" translation="0,0,0">
                  <Inline url="body_7.x3d"/>
                  <Transform DEF="body_8" translation="0.5,0,0">
                     <Inline url="body_8.x3d"/>
                     <Transform DEF="body_9" translation="0,0,0">
                        <Inline url="body_9.x3d"/>
                     </Transform>
                  </Transform>
               </Transform>
            </Transform>
         </Transform>
      </Transform>
   </Scene>
</X3D>

but stylesheet has not any effect or there is too much nodes. I understand that a lot depends on the parameters that I specify in the select attribute, but I still have not been able to find the solution How can I rich it?

Next I try to give an examplse of use it. In the following code I use a list of strings that is formed in tamplate "getlistOfBodies"

<xsl:variable name="listOfBodies">
        <xsl:call-template name="getlistOfBodies">
        </xsl:call-template>
      </xsl:variable>
      
      <xsl:template match="*[Transform]">  
        <xsl:call-template name="wrap">
          <xsl:with-param name="search" select="$listOfBodies" /> 
            <!-- examples of $listOfBodies: "'body_2', 'body_5','body_6'" -->
        </xsl:call-template>
      </xsl:template>

  <xsl:template name="wrap" > 
    <xsl:param name="search" select="0"/>
    <xsl:copy>
     <xsl:for-each-group select="* | @*" group-adjacent="@DEF = $search">
    <!-- ... ->

So I want to see next output depending on the $search: (let $search = "'body_2','body_6'")

<?xml version="1.0" encoding="UTF-8"?>
    <X3D version="3.2">
        <Scene>
          <Background skyColor="1 1 1"/>
          <Viewpoint position="0 0 4"/>
          <Transform DEF="body_0">
             <Inline url="body_0.x3d"/>
             <Transform DEF="body_1" translation="0,0.2,0">
                <Inline url="body_1.x3d"/>
    			
                <Transform rotation="1 0 0 1.57">
                   <Transform DEF="body_2" translation="0,0,0">
                      <Inline url="body_2.x3d"/>
                      <Transform DEF="body_3" translation="0,0,0">
                         <Inline url="body_3.x3d"/>
                         <Transform DEF="body_4" translation="0.5,0,0">
                            <Inline url="body_4.x3d"/>
                            <Transform DEF="body_5" translation="0,0,0">
                                    <Inline url="body_5.x3d"/>
                                </Transform>
                         </Transform>
                      </Transform>
                    </Transform>
    			</Transform>
    			
    			<Transform rotation="1 0 0 1.57">
                   <Transform DEF="body_6" translation="0,0,0">
                      <Inline url="body_6.x3d"/>
                      <Transform DEF="body_7" translation="0,0,0">
                         <Inline url="body_7.x3d"/>
                         <Transform DEF="body_8" translation="0.5,0,0">
                            <Inline url="body_8.x3d"/>
                            <Transform DEF="body_9" translation="0,0,0">
                               <Inline url="body_9.x3d"/>
                            </Transform>
                         </Transform>
                      </Transform>
                   </Transform>
                </Transform>
                
             </Transform>
          </Transform>
       </Scene>
    </X3D>

If $search = "'body_2','body_3'" then should be wrap two nodes (DEF='body_2', DEF='body_3') with two different Translates. If the number of strings in the variable is 6, then six nodes should be wrap with six Transforms. It's something like this...

Upvotes: 0

Views: 40

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167506

If you use your grouping at the right level and handle the rest by the identity transformation (for instance in XSLT 3 with xsl:mode on-no-match="shallow-copy") then you get

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:param name="search" as="xs:string">body_3</xsl:param>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="*[Transform/@DEF = $search]">
      <xsl:copy>
          <xsl:for-each-group select="*" group-adjacent="@DEF = $search">
            <xsl:choose>
              <xsl:when test="current-grouping-key()">
                <Transform rotation="1 0 0 1.57">
                  <xsl:apply-templates select="current-group()"/>
                </Transform>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="current-group()"/>
              </xsl:otherwise>
            </xsl:choose>              
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Based on your comments and edits it sounds as if you don't want to wrap adjacent elements at all but simply want to wrap certain elements into a new Transform, as the following code does:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:param name="def-list" as="xs:string*" select="'body_2','body_3'"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Transform[@DEF = $def-list]">
      <Transform rotation="1 0 0 1.57">
          <xsl:next-match/>
      </Transform>
  </xsl:template>

</xsl:stylesheet>

So the parameter is now a sequence of strings (xs:string*) and any Transform element having a DEF attribute equal to one of the values in the sequence is wrapped into a new Transform element.

Online sample at https://xsltfiddle.liberty-development.net/gWcDMeu

Upvotes: 1

Related Questions