Filter out certain XML nodes and form another XML with these nodes and their ancestors and child nodes

Viewed 113

I have below XML data and I need to filter certain XML nodes and form another XML data with those nodes and it's ancestor nodes as well as all of it's child nodes. I am new to XSLT and tried different approach using XSLT but nothing is working.

Can this be achieved in XSLT or not?

Data:

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>`enter code here`
   <challengeDetails>
      <challengeId>1000000017</challengeId>
      <challengeCode>WEEKACTIVE</challengeCode>
      <challengeCategory>StayActive</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000064</periodId>
         <periodNumber>2</periodNumber>
         <periodStatus>INPROGRESS</periodStatus>
         <challengeAwardIssued>
            <awardType>GAME</awardType>
            <awardCode>GCH009</awardCode>
         </challengeAwardIssued>
         <challengeAwardIssued>
            <awardType>REWARD</awardType>
            <awardCode>EHNC001</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
      <challengePeriodsDetails>
         <periodId>1000000065</periodId>
         <periodNumber>3</periodNumber>
         <periodStatus>COMPLETED</periodStatus>
         <challengeAwardIssued>
            <awardType>REWARD</awardType>
            <awardCode>EHNC002</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
   <challengeDetails>
      <challengeId>1000000018</challengeId>
      <challengeCode>QUITSUGAR</challengeCode>
      <challengeCategory>QuitSugar</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000066</periodId>
         <periodNumber>2</periodNumber>
         <periodStatus>INPROGRESS</periodStatus>
         <challengeAwardIssued>
            <awardType>REWARD</awardType>
            <awardCode>EHNC001</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
      <challengePeriodsDetails>
         <periodId>1000000067</periodId>
         <periodNumber>3</periodNumber>
         <periodStatus>COMPLETED</periodStatus>
         <challengeAwardIssued>
            <awardType>GAME</awardType>
            <awardCode>EHNC001</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
</MessageDetails>

Search Criteria #1: awardType = GAME

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>
   <challengeDetails>
      <challengeId>1000000017</challengeId>
      <challengeCode>WEEKACTIVE</challengeCode>
      <challengeCategory>StayActive</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000064</periodId>
         <periodNumber>2</periodNumber>
         <periodStatus>INPROGRESS</periodStatus>
         <challengeAwardIssued>
            <awardType>GAME</awardType>
            <awardCode>GCH009</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
   <challengeDetails>
      <challengeId>1000000018</challengeId>
      <challengeCode>QUITSUGAR</challengeCode>
      <challengeCategory>QuitSugar</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000067</periodId>
         <periodNumber>3</periodNumber>
         <periodStatus>COMPLETED</periodStatus>
         <challengeAwardIssued>
            <awardType>GAME</awardType>
            <awardCode>GCH008</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
</MessageDetails>

Search Criteria #2: awardType = GAME and periodStatus = COMPLETED and challengeCode = QUITSUGAR

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>
   <challengeDetails>
      <challengeId>1000000018</challengeId>
      <challengeCode>QUITSUGAR</challengeCode>
      <challengeCategory>QuitSugar</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000067</periodId>
         <periodNumber>3</periodNumber>
         <periodStatus>COMPLETED</periodStatus>
         <challengeAwardIssued>
            <awardType>GAME</awardType>
            <awardCode>GCH008</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
</MessageDetails>

Search Criteria #3: challengeCode = WEEKACTIVE & periodId = 1000000064

    <?xml version="1.0" encoding="UTF-8"?>
    <MessageDetails>
       <challengeDetails>
          <challengeId>1000000017</challengeId>
          <challengeCode>WEEKACTIVE</challengeCode>
          <challengeCategory>StayActive</challengeCategory>
          <challengePeriodsDetails>
             <periodId>1000000064</periodId>
             <periodNumber>2</periodNumber>
             <periodStatus>INPROGRESS</periodStatus>
             <challengeAwardIssued>
                <awardType>GAME</awardType>
                <awardCode>GCH009</awardCode>
             </challengeAwardIssued>
          </challengePeriodsDetails>
       </challengeDetails>
    </MessageDetails>

Search Criteria #4: periodId = 1000000066

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>
   <challengeDetails>
      <challengeId>1000000018</challengeId>
      <challengeCode>QUITSUGAR</challengeCode>
      <challengeCategory>QuitSugar</challengeCategory>
      <challengePeriodsDetails>
         <periodId>1000000066</periodId>
         <periodNumber>2</periodNumber>
         <periodStatus>INPROGRESS</periodStatus>
         <challengeAwardIssued>
            <awardType>REWARD</awardType>
            <awardCode>EHNC001</awardCode>
         </challengeAwardIssued>
      </challengePeriodsDetails>
   </challengeDetails>
</MessageDetails>

Search Criteria #5: awardType = 'GAME' and awardCode= 'EHNC001'

Required output:

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>`enter code here`
       <challengeDetails>
          <challengeId>1000000018</challengeId>
          <challengeCode>QUITSUGAR</challengeCode>
          <challengeCategory>QuitSugar</challengeCategory>
          <challengePeriodsDetails>
             <periodId>1000000067</periodId>
             <periodNumber>3</periodNumber>
             <periodStatus>COMPLETED</periodStatus>
             <challengeAwardIssued>
                <awardType>GAME</awardType>
                <awardCode>EHNC001</awardCode>
             </challengeAwardIssued>
          </challengePeriodsDetails>
       </challengeDetails>
    </MessageDetails>

Output received:

<?xml version="1.0" encoding="UTF-8"?>
<MessageDetails>`enter code here`
       <challengeDetails>
          <challengeId>1000000017</challengeId>
          <challengeCode>WEEKACTIVE</challengeCode>
          <challengeCategory>StayActive</challengeCategory>
          <challengePeriodsDetails>
             <periodId>1000000064</periodId>
             <periodNumber>2</periodNumber>
             <periodStatus>INPROGRESS</periodStatus>
          </challengePeriodsDetails>
       </challengeDetails>
       <challengeDetails>
          <challengeId>1000000018</challengeId>
          <challengeCode>QUITSUGAR</challengeCode>
          <challengeCategory>QuitSugar</challengeCategory>
          <challengePeriodsDetails>
             <periodId>1000000067</periodId>
             <periodNumber>3</periodNumber>
             <periodStatus>COMPLETED</periodStatus>
             <challengeAwardIssued>
                <awardType>GAME</awardType>
                <awardCode>EHNC001</awardCode>
             </challengeAwardIssued>
          </challengePeriodsDetails>
       </challengeDetails>
    </MessageDetails>

XSLT used:

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

<xsl:template match="/MessageDetails/challengeDetails[not(challengePeriodsDetails/challengeAwardIssued/awardType = 'GAME' and challengePeriodsDetails/challengeAwardIssued/awardCode= 'EHNC001')]" />

<xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[not(challengeAwardIssued/awardType = 'GAME' and challengeAwardIssued/awardCode= 'EHNC001')]" />

<xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails/challengeAwardIssued[not(awardType = 'GAME' and awardCode= 'EHNC001')]" />

<xsl:mode on-no-match="shallow-copy" />
<xsl:output method="xml" indent="yes" />

</xsl:stylesheet>
1 Answers

From what I understand, you want to filter out challengeDetails and challengePeriodsDetails given some predicates. To filter out elements in XSL, you just have to provide empty templates matching the elements you don't want.

Query 1

This is a nice and simple query for starting. Just start from the copy-all template node()|@*, and add two rules to filter out what's not needed.

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

  <xsl:template match="/MessageDetails/challengeDetails[
      not(challengePeriodsDetails/challengeAwardIssued/awardType = 'GAME')
    ]" />

  <xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[
      not(challengeAwardIssued/awardType = 'GAME')
    ]" />

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

</xsl:stylesheet>

Query 2

The crux here is to filter out the challengePeriodsDetails not satisfying both of the predicates. We work on these elements, not on challengeDetails, which is why the predicate contains another predicate. Without it we would filter out too much data.

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

  <xsl:template match="/MessageDetails/challengeDetails[
      not(challengePeriodsDetails[
        challengeAwardIssued/awardType = 'GAME' and periodStatus = 'COMPLETED'
      ])
    ]" />

  <xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[
      not(challengeAwardIssued/awardType = 'GAME' and periodStatus = 'COMPLETED')
    ]" />

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

</xsl:stylesheet>

Query 3

The particularity here is that we do not need to worry about challengeCode in the template about challengePeriodsDetails, since its parent would not appear in the final document anyway.

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

  <xsl:template match="/MessageDetails/challengeDetails[
      not(challengeCode = 'WEEKACTIVE' and challengePeriodsDetails/periodId = '1000000064')
    ]" />

  <xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[
      not(periodId = '1000000064')
    ]" />

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

</xsl:stylesheet>

Query 4

This one looks like query 1, I can only recommend that you try to do it yourself before looking at the code to prove yourself you have improved.

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

  <xsl:template match="/MessageDetails/challengeDetails[not(challengePeriodsDetails/periodId = '1000000066')]" />

  <xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[not(periodId = '1000000066')]" />

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

</xsl:stylesheet>

Query 5

You were so close! The trick is to test awardType and awardCode for the same challengeAwardIssued. You have to use a test on this node. Here is a solution:

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

  <xsl:template match="/MessageDetails/challengeDetails[
      not(challengePeriodsDetails/challengeAwardIssued[
        awardType = 'GAME' and awardCode = 'EHNC001'
      ])
    ]" />

  <xsl:template match="/MessageDetails/challengeDetails/challengePeriodsDetails[
      not(challengeAwardIssued[
        awardType = 'GAME' and awardCode = 'EHNC001'
      ])
    ]" />

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

</xsl:stylesheet>
Related