1

Does any one know how to make the following transformation using xslt?

Source code:

<?xml version="1.0" encoding="UTF-8"?>
<body>
  <termEntry id="1">
   <langSet lang="eng-us">
    <ntig>
     <termGrp>
      <term></term>
     </termGrp>
    </ntig>
    <ntig>
     <termGrp>
      <term></term>
     </termGrp>
    </ntig>
   </langSet>

   <langSet lang="ara-ae">
    <ntig>
     <termGrp>
      <term>123</term>
     </termGrp>
    </ntig>
   </langSet>
</termEntry>

<termEntry id="2">
 <langSet lang="eng-us">
  <ntig>
   <termGrp>
    <term></term>
   </termGrp>
  </ntig>
  <ntig>
   <termGrp>
    <term></term>
   </termGrp>
  </ntig>
  <ntig>
   <termGrp>
    <term>123</term>
   </termGrp>
  </ntig>
 </langSet>
</termEntry>
</body>

Request: 1.if the value in <term></term> is null\empty, delete its grandparent node, namely

<ntig></ntig>

2.In this way, if all <term> tags are empty, delete the whole <langset> node.

Expected result

<?xml version="1.0" encoding="UTF-8"?>
<body>
  <termEntry id="1">

   <langSet lang="ara-ae">
    <ntig>
     <termGrp>
      <term>123</term>
     </termGrp>
    </ntig>
   </langSet>
  </termEntry>

  <termEntry id="2">
   <langSet lang="eng-us">
    <ntig>
     <termGrp>
      <term>123</term>
     </termGrp>
    </ntig>
   </langSet>
 </termEntry>
</body>
1
  • Good question, +1. See my solution for a complete, short and easy solution, that unlike the other answer, strictly implements the requirements of the question. :) Commented Mar 21, 2011 at 13:05

2 Answers 2

3

The identity transform plus a couple of simple empty templates is what you need here. You want to copy all your input to the output unless it meets your criteria, in which case you want to suppress it.

A stylesheet such as:

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

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

    <xsl:template match="ntig[descendant::term[. = '']]"/>
    <xsl:template match="langSet[not(descendant::term[. != ''])]"/>

</xsl:stylesheet>

will do what you need. The template which matches ntig elements will suppress those elements with empty term grandchildren. The template which matches langSet elements suppresses those langSets where there are no descendant term elements which have content.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi Nic, I wanted to test by adding '<?xml-stylesheet type="text/xsl" href="FM_Nic.xsl"?> ' And after click the xml, only text values are displayed 123 123 Instead of expected codes... Do I need to add more references?
By the way, I wonder if I can generate the new xml instead of viewing. And How? By not using other programing langugages, say java
@Jackie - see @Dimitre's answer for a good explanation of why this is happening.
1

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="ntig[not(*/term[string-length()>0])]"/>
 <xsl:template match="langSet[not(*/*/term[string-length()>0])]"/>
</xsl:stylesheet>

when applied on the provided XML document:

<body>
    <termEntry id="1">
        <langSet lang="eng-us">
            <ntig>
                <termGrp>
                    <term></term>
                </termGrp>
            </ntig>
            <ntig>
                <termGrp>
                    <term></term>
                </termGrp>
            </ntig>
        </langSet>
        <langSet lang="ara-ae">
            <ntig>
                <termGrp>
                    <term>123</term>
                </termGrp>
            </ntig>
        </langSet>
    </termEntry>
    <termEntry id="2">
        <langSet lang="eng-us">
            <ntig>
                <termGrp>
                    <term></term>
                </termGrp>
            </ntig>
            <ntig>
                <termGrp>
                    <term></term>
                </termGrp>
            </ntig>
            <ntig>
                <termGrp>
                    <term>123</term>
                </termGrp>
            </ntig>
        </langSet>
    </termEntry>
</body>

produces the wanted result:

<body>
   <termEntry id="1">
      <langSet lang="ara-ae">
         <ntig>
            <termGrp>
               <term>123</term>
            </termGrp>
         </ntig>
      </langSet>
   </termEntry>
   <termEntry id="2">
      <langSet lang="eng-us">
         <ntig>
            <termGrp>
               <term>123</term>
            </termGrp>
         </ntig>
      </langSet>
   </termEntry>
</body>

Explanation:

  1. The identity rule (template) copies every node "as-is".

  2. The template overriding the identity rule for ntig[not(*/term[string-length()>0])] has an empty body -- this effectively ignores (deletes) any ntig element that doesn't have at lest one term grandchild with positive string-length().

  3. The template overriding the identity rule for langSet[not(*/*/term[string-length()>0])] has an empty body -- this effectively ignores (deletes) any langSet element that doesn't have at lest one term great-grandchild with positive string-length().

Do note that specifying templates like this violates the definition of the problem:

<xsl:template match="ntig[descendant::term[. = '']]"/> 
<xsl:template match="langSet[not(descendant::term[. != ''])]"/>   

Because the requirement is to "if the value in is null\empty, delete its grandparent node".

However, the first template above deletes not only the grand-parent ntig, but any ancestor ntig.

The current solution does not commit such a mistake.

9 Comments

@Dimitre: Hi Dimitre, I wanted to test by adding '<?xml-stylesheet type="text/xsl" href="FM_Dimi.xsl"?> ' And after click the xml, only text values are displayed 123 123 Instead of expected codes... Do I need to add more references? By the way, I wonder if I can generate the new xml instead of viewing. And How? By not using other programing langugages, say java
@Jackie: this is because the PI gets re-generated and IE (and probably other browsers) interprete this as an endless loop... The proper way to develop with XSLT is by using an XSLT IDE. I am using both XSelerator and oXygen and I never use a browser to run/test an XSLT transformation. Other, uglier ways are to invoke the particular XSLT processor from the command line.
@Dimitre, +1 for being absolutely right although I suspect you are splitting hairs in this particular case :)
@Nic-Gibson: Following the OP's requirements as closely as possible is not splitting hairs -- not following them exactly enforces inexact practices and may indicate to people that XPath is not powerful enough to deal with the exact requirements.
@Jackie: To prevent this behavior of the in-browser transformation, add this template: <xsl:template match="processing-instruction()"/>
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.