Home > Net >  How to remove extra tag?
How to remove extra tag?

Time:05-16

First xml file to be updated:

<Conf key="11" title="tit">
  <Item key="1" >
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="11" description="11" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50"  />
      <Widget title="22" description="22" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="33" description="33"  />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="44" description="44" />
    </Am>
  </Item>
</Conf>

The second xml file "updates.xml" from which data is taken to update the first file:

<Profile title="u">
  <Item key="1">
    <Vol>
      <Widget title="AA"/>
    </Vol>
    <Am>
      <Widget title="BB" description="BB" />
    </Am>
  </Item>
  <Item key="2">
    <Vol>
      <Widget title="CC" description="CC" />
    </Vol>
    <Am>
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Profile>

The update.xml file differs from the input file in that it contains only those tags that are ancestors of the Widget tag and lacks some attributes. After the XSLT1.0 transformation, the input tree is transferred to the output tree with the replacement of the Widget tags from the "updates.xml" file. If there is a Widget tag in the input file, but not in the "updates.xml" file, then the widget tag from the input file is transferred to the output tree. Now the transformation file:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  exclude-result-prefixes="exslt"
  version="1.0">
  <xsl:output method="xml"/>

  <xsl:param name="updates" select="document('updates.xml')"/>
  <xsl:key name="replacement" match="Widget" use="local-name(parent::*)"/>

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

  <xsl:template match="Widget">
    <xsl:variable name="parentName" select="local-name(parent::*)"/>
    <xsl:variable name="replacement">
      <xsl:for-each select="$updates">
        <xsl:copy-of select="key('replacement', $parentName)"/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="exslt:node-set($replacement)/*">
        <xsl:copy-of select="$replacement"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

After transforming the file:

<?xml version="1.0" encoding="utf-8"?>
<Conf key="11" title="tit">
  <Item key="1">
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="AA" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50" />
      <Widget title="BB" description="BB" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="AA" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="BB" description="BB" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Conf>

How to ensure that after the XSLT1.0 transformation, the output file does not have extra Widget tags and the output file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Conf key="11" title="tit">
  <Item key="1">
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="AA" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50" />
      <Widget title="BB" description="BB" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Conf>

CodePudding user response:

I would approach it this way, and don't need to use the exslt:node-set() extension function.

Create a composite key that uses the @key of the Item and the local-name() of the Widget parent element. Use that composite key value to lookup the corresponding Widget from the $updates in the xs:for-each that is used to switch context to the updates.xml document.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common"
    exclude-result-prefixes="exslt"
    version="1.0">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:variable name="updates" select="document('updates.xml')"/>
    <xsl:key name="replacement" match="Widget" use="concat(../../@key, local-name(..))"/>
    
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Widget">
        <xsl:variable name="key" select="concat(../../@key, local-name(..))"/>
        <xsl:variable name="replacement">
            <xsl:for-each select="$updates">
                <xsl:copy-of select="key('replacement', $key)"/>
            </xsl:for-each>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="$replacement">
                <xsl:copy-of select="$replacement"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
</xsl:stylesheet>
  • Related