Home > Back-end >  How to add a merge template for two xml files?
How to add a merge template for two xml files?

Time:03-03

First xml file in.xml:

<conf title="co" attr01="val01">
  <c attr02="val02">
    <Item key="db" attr03="val03">
    <m1 />
      <s>
        <m2 />
        <Item key="1">
        <m3 />
          <sp>
            <Chart />
          </sp>
        </Item>
        <m4 />
        <Item key="2">
          <sp>
            <m5 />
            <Chart />
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart />
          </sp>
        </Item>
        <m6 />
      </s>
    </Item>
  </c>
</conf>

The second xml file profile.xml, which completes the first file with information about the widgets:

<p title="profile">
  <c>
    <Item key="db">
      <s>
        <Item key="1">
          <sp>
            <Chart>
                <Widget title="widget12">
                    <Test title="test1"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="2">
          <sp>
            <Chart>
                <Widget title="widget32">
                    <Test title="test3"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart>
                <Widget title="widget54">
                    <Test title="test6"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
      </s>
    </Item>
  </c>
</p>

The profile.xml file differs from in.xml in that it has a different root tag name, each Chart tag has a corresponding child Widget tag, only parent tags relative to the Chart tag are present in the file and all tags except the root and Widget have only key attributes.

After the transformation, the resulting file differs from the original one only in the Widget child tags of the Chart tag:

<conf title="co" attr01="val01">
  <c attr02="val02">
    <Item key="db" attr03="val03">
    <m1 />
      <s>
        <m2 />
        <Item key="1">
        <m3 />
          <sp>
            <Chart>
                <Widget title="widget12">
                    <Test title="test1"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <m4 />
        <Item key="2">
          <sp>
            <m5 />
            <Chart>
                <Widget title="widget32">
                    <Test title="test3"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <Item key="3">
          <sp>
            <Chart>
                <Widget title="widget54">
                    <Test title="test6"/>
                </Widget>
            </Chart>
          </sp>
        </Item>
        <m6 />
      </s>
    </Item>
  </c>
</conf>

There is now an incomplete XSLT1.0 transformation file:

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

  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:param name="updates" select="document($fileName)" />

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

How to rewrite the "p" template XSLT1.0?

CodePudding user response:

Try this:

convert.xsl

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:param name="updates" select="document($fileName)" />
  
  <!--transform starts here on input XML-->
  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  
  <!--rules for general nodes()-->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!--special rule for Chart elements-->      
  <xsl:template match="Chart">
    <xsl:variable name="context" select="."/>
    <xsl:if test="$updates//Item/@key = $context/ancestor-or-self::Item[1]/@key">
      <xsl:copy-of select="($updates//Item[@key = $context/ancestor-or-self::Item[1]/@key])//Chart"/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

output.xml

<?xml version="1.0" encoding="UTF-8"?>
<conf title="co" attr01="val01">
   <c attr02="val02">
      <Item key="db" attr03="val03">
         <m1/>
         <s>
            <m2/>
            <Item key="1">
               <m3/>
               <sp>
                  <Chart>
                     <Widget title="widget12">
                        <Test title="test1"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m4/>
            <Item key="2">
               <sp>
                  <m5/>
                  <Chart>
                     <Widget title="widget32">
                        <Test title="test3"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <Item key="3">
               <sp>
                  <Chart>
                     <Widget title="widget54">
                        <Test title="test6"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m6/>
         </s>
      </Item>
   </c>
</conf>

You may change the param profile.xml to some other, if needed...

CodePudding user response:

Without using key

If your attributes @attr01-@attr03 do matter?:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:variable name="updates" select="document($fileName)/*" />
  
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="Chart">
    <xsl:variable name="idattr01" select="ancestor::*[@attr01]/@attr01"/>
    <xsl:variable name="idattr02" select="ancestor::*[@attr02]/@attr02"/>
    <xsl:variable name="idattr03" select="ancestor::*[@attr03]/@attr03"/>
    <xsl:variable name="key"      select="ancestor::*[@key][1]/@key"/>
    <xsl:variable name="chartFromUpdate" select="$updates[@attr01=$idattr01]/c[@attr02=$idattr02]/Item[@attr03=$idattr03]/s/Item[@key=$key]/sp/Chart" />
    <xsl:choose>
      <xsl:when test="$chartFromUpdate">
        <xsl:copy-of select="$chartFromUpdate"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Or you could also use the key-function like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  
  <xsl:param name="fileName" select="'profile.xml'" />
  <xsl:variable name="updates" select="document($fileName)" />
  
  <xsl:key name="item" match="Item" use="@key"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="Chart">
    <xsl:variable name="key"      select="ancestor::*[@key][1]/@key"/>
    <xsl:variable name="chartFromUpdate">
      <!-- Since the lookup is in an different document we have to change the context -->
      <xsl:for-each select="$updates">
        <xsl:copy-of select="key('item', $key)/sp/Chart"/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$chartFromUpdate">
        <xsl:copy-of select="$chartFromUpdate"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>    
  </xsl:template>
  
</xsl:stylesheet>

On using the key-function on a different document using xslt 1.0 see this answer

For both xslt's this is the output:

<?xml version="1.0" encoding="UTF-8"?>
<conf title="co" attr01="val01">
   <c attr02="val02">
      <Item key="db" attr03="val03">
         <m1/>
         <s>
            <m2/>
            <Item key="1">
               <m3/>
               <sp>
                  <Chart>
                     <Widget title="widget12">
                        <Test title="test1"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m4/>
            <Item key="2">
               <sp>
                  <m5/>
                  <Chart>
                     <Widget title="widget32">
                        <Test title="test3"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <Item key="3">
               <sp>
                  <Chart>
                     <Widget title="widget54">
                        <Test title="test6"/>
                     </Widget>
                  </Chart>
               </sp>
            </Item>
            <m6/>
         </s>
      </Item>
   </c>
</conf>
  • Related