2

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>
Nijith
  • 69
  • 6
  • I would approach with XML and XPath in your program code. Read each criteria XML, and mark the matching elements in the original XML. Then remove all elements without a match, and save the XML as a copy. – William Walseth Dec 06 '20 at 15:11
  • Thanks @WilliamWalseth for the reply. Let me check on this as well. – Nijith Dec 07 '20 at 08:16

1 Answers1

1

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>
Fabian Pijcke
  • 2,920
  • 25
  • 29
  • 1
    Thanks @Fabian . Its working. Could you please help to figure out the scenario #5 added above. With the XSLT I am using it seems like for challengeDetails and challengePeriodDetails nodes, awardType = 'GAME' and awardCode= 'EHNC001' is being matched from multiple challengAwardIssued nodes and hence first challengeDetails and challengePeriodDetails block is coming in output. – Nijith Dec 09 '20 at 08:41
  • Almost! I updated my answer accordingly :-) – Fabian Pijcke Dec 09 '20 at 09:01
  • You're very welcome! Please accept the answer using the mark, so that other members of the site know they should not focus anymore on this question :-) – Fabian Pijcke Dec 09 '20 at 12:14