Search code examples
xsltattributesnodes

How to remove XML nodes depending attribute value of other nodes


I have an XML looking like this:

  <pack>
    <titlesPacks>
      <StoryPack id="1111111">
        <value>A</value>
      </StoryPack>
      <StoryPack id="2222222">
        <value>F</value>
      </StoryPack>
    </titlesPacks>
    <referenceTable>
       <TitleReference id="1111111" />
       <TitleReference id="2222222" />
    </referenceTable>
  </pack>

I need to copy the xml file, but:

  • delete StoryPack nodes where value node has specific value (A, B, C for example). This part is already OK

  • delete 'TitleReferencenodes whereidattribute value is equal toid attribute value of' StoryPack nodes deleted above

I don't know how to do the second one: I tried with Key, but doesn't work:

My current XSL:

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

    <xsl:key name="titleId" match="StoryPack" use="@id"/>

    <!-- Values for which nodes must be deleted -->
    <data:data xmlns="">
        <value>A</value>
        <value>B</value>
        <value>C</value>
    </data:data>

    <xsl:variable name="values" select="document('')/xsl:stylesheet/data:data/value"/>


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


<!-- Delete nodes with specific values -->
    <xsl:template match="StoryPack[value = $values]"/>

<!-- Dlete nodes with id from specific values -->
<xsl:template match="TitleReference[StoryPack[key('titleId', @id)/value = $values]]" />
</xsl:stylesheet>

Thank you for your help!

=============

Following @michael.hor257k answers : my source XML is a little more complex, that's probably why it doesn't work in my case: The Xpath of value is deeper: /Pack/titlesPacks/StoryPack/assets/TitleAssets/assets/StringAssetInfo/value So I've done:

<xsl:template match="TitleReference[key('titleId', @id)/assets/TitleAssets/assets/StringAssetInfo[@attrId = '127']/value = $values]" />
               

but it doesn't work.

XML:

<?xml version="1.0" encoding="UTF-8"?>
<Pack>
  <titlesPacks>
    <StoryPack id="1111111">
      <assets>
        <TitleAssets>
             <assets>
               <StringAssetInfo attrId="127">
                  <value>A</value>
               </StringAssetInfo>
             </assets>
        </TitleAssets>
      </assets>
     </StoryPack>
    </titlesPacks>
    <referenceTable>
     <ReferenceTable>
      <titlesReferences>
        <TitleReference id = "1111111"/>
      </titlesReferences>
     </ReferenceTable>
    </referenceTable>
</pack>

my need is to

  • delete StoryPack where attribute attrId of StringAssetInfo = 127 and value = A, or B... => Already OK
  • delete TitleReference where id attribute value = id attribute value of deleted nodes above

Solution

  • Try it this way?

    XSLT 2.0

    <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:key name="titleId" match="StoryPack" use="@id"/>
    
    <!-- Values for which nodes must be deleted -->
    <xsl:variable name="values">
        <value>A</value>
        <value>B</value>
        <value>C</value>
    </xsl:variable>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Delete nodes with specific values -->
    <xsl:template match="StoryPack[value = $values/value]"/>
    
    <!-- Delete nodes with id from specific values -->
    <xsl:template match="TitleReference[key('titleId', @id)/value = $values/value]" />
    
    </xsl:stylesheet>
    

    I have simplified the variable definition, so it can work in this online demo: https://xsltfiddle.liberty-development.net/jz1PuP9 - and also because it's simpler.


    Added:

    In view of your updated XML structure, change:

    <xsl:template match="StoryPack[value = $values/value]"/>
    

    to:

    <xsl:template match="StoryPack[.//value = $values/value]"/>
    

    or (preferably) to:

    <xsl:template match="StoryPack[assets/TitleAssets/assets/StringAssetInfo/value = $values/value]"/>
    

    and change:

    <xsl:template match="TitleReference[key('titleId', @id)/value = $values/value]" />
    

    to:

    <xsl:template match="TitleReference[key('titleId', @id)//value = $values/value]" />
    

    or (preferably) to:

    <xsl:template match="TitleReference[key('titleId', @id)/assets/TitleAssets/assets/StringAssetInfo/value = $values/value]" />