2

If I have this input file:

<root> 
    <node id="N1">
        <fruit id="small_fruit">
            <orange id="1" action="create">
                <attribute>
                    <color>yellow</color>
                </attribute>
            </orange>
        </fruit>

        <fruit id="large_fruit" action="create">
            <melon id="1" action="destroy">
                <attribute>
                    <color>green</color>
                </attribute>
            </melon>
        </fruit>            
    </node>

    <node id="N2">
        <dog id="small_dog">    
            <poodle id="1" action="create">
                <attribute>
                    <color>Yellow</color>    
                </attribute>
            </poodle>       

            <poodle id="1" action="change">
                <attribute>
                    <color>Brown</color>        
                </attribute>
            </poodle>

            <terrier id="2" action="destroy">
                <attribute>
                    <color>Blue</color>    
                </attribute>
            </terrier>
        </dog>                          
    </node>
</root>

and I need to separate into two files: output1: node_destroy.xml

<root> 
    <node id="N1">          
        <fruit id="large_fruit" action="create">
            <melon id="1" action="destroy">
                <attribute>
                    <color>green</color>
                </attribute>
            </melon>
        </fruit>            
    </node>

    <node id="N2">
        <dog id="small_dog">                
            <terrier id="2" action="destroy">
                <attribute>
                    <color>Blue</color>    
                </attribute>
            </terrier>
        </dog>                          
    </node>
</root>

output2: node_other_than_destroy.xml

<root> 
    <node id="N1">
        <fruit id="small_fruit">
            <orange id="1" action="create">
                <attribute>
                    <color>yellow</color>
                </attribute>
            </orange>
        </fruit>                  
    </node>

    <node id="N2">
        <dog id="small_dog">    
            <poodle id="1" action="create">
                <attribute>
                    <color>Yellow</color>    
                </attribute>
            </poodle>       

            <poodle id="1" action="change">
                <attribute>
                    <color>Brown</color>        
                </attribute>
            </poodle>
        </dog>                          
    </node>
</root>

Basically I need to remove the node with action='destroy' to one file and other node with other action into another file. (the checked action-node is melon, poodle, terrier NOT their parents, i.e. fruit and dog)

Please help me with the transformation file. Thanks very much.

regards, John

0

2 Answers 2

2

In XSLT 1.0 there isnt a way to create more than one outputs from one execution of a transformation.

Here are two separate XSLT 1.0 transformations:

The first produces only the result with action="destroy:

<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="node[not(*/*/@action = 'destroy')]"/>
 <xsl:template match="node/*[not(*/@action = 'destroy')]"/>

 <xsl:template match="node/*/*[not(@action = 'destroy')]"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<root>
    <node id="N1">
        <fruit id="small_fruit">
            <orange id="1" action="create">
                <attribute>
                    <color>yellow</color>
                </attribute>
            </orange>
        </fruit>

        <fruit id="large_fruit" action="create">
            <melon id="1" action="destroy">
                <attribute>
                    <color>green</color>
                </attribute>
            </melon>
        </fruit>
    </node>

    <node id="N2">
        <dog id="small_dog">
            <poodle id="1" action="create">
                <attribute>
                    <color>Yellow</color>
                </attribute>
            </poodle>

            <poodle id="1" action="change">
                <attribute>
                    <color>Brown</color>
                </attribute>
            </poodle>

            <terrier id="2" action="destroy">
                <attribute>
                    <color>Blue</color>
                </attribute>
            </terrier>
        </dog>
    </node>
</root>

the wanted, correct result is produced:

<root>
   <node id="N1">
      <fruit id="large_fruit" action="create">
         <melon id="1" action="destroy">
            <attribute>
               <color>green</color>
            </attribute>
         </melon>
      </fruit>
   </node>
   <node id="N2">
      <dog id="small_dog">
         <terrier id="2" action="destroy">
            <attribute>
               <color>Blue</color>
            </attribute>
         </terrier>
      </dog>
   </node>
</root>

The second transformation producess all "non-destroy" content:

<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="node[not(*/*/@action[not(. = 'destroy')])]"/>
 <xsl:template match="node/*[not(*/@action[not(. = 'destroy')])]"/>

 <xsl:template match="node/*/*[@action = 'destroy']"/>
</xsl:stylesheet>

when this transformation is applied on the same XML document (above), the wanted result is produced:

<root>
   <node id="N1">
      <fruit id="small_fruit">
         <orange id="1" action="create">
            <attribute>
               <color>yellow</color>
            </attribute>
         </orange>
      </fruit>
   </node>
   <node id="N2">
      <dog id="small_dog">
         <poodle id="1" action="create">
            <attribute>
               <color>Yellow</color>
            </attribute>
         </poodle>
         <poodle id="1" action="change">
            <attribute>
               <color>Brown</color>
            </attribute>
         </poodle>
      </dog>
   </node>
</root>

II. XSLT 2.0 Solution -- creating both output files within the same transformation:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="/">
   <xsl:variable name="vResultDestroyed">
     <xsl:apply-templates mode="destroyed"/>
   </xsl:variable>
   <xsl:variable name="vResultNonDestroyed">
     <xsl:apply-templates mode="non-destroyed"/>
   </xsl:variable>

   <xsl:for-each select="1 to 2">
     <xsl:result-document
        href="file:///c:temp/delete/{'non-'[position()=current()]}destroyed.xml">
      <xsl:copy-of select=
       "($vResultNonDestroyed, $vResultDestroyed)[position() = current()]"/>
     </xsl:result-document>
   </xsl:for-each>
 </xsl:template>

 <xsl:template mode="destroyed" match="node[not(*/*/@action = 'destroy')]"/>
 <xsl:template mode="destroyed" match="node/*[not(*/@action = 'destroy')]"/>
 <xsl:template mode="destroyed" match="node/*/*[not(@action = 'destroy')]"/>

 <xsl:template mode="non-destroyed"  match="node[not(*/*/@action[not(. = 'destroy')])]"/>
 <xsl:template mode="non-destroyed"  match="node/*[not(*/@action[not(. = 'destroy')])]"/>
 <xsl:template mode="non-destroyed"  match="node/*/*[@action = 'destroy']"/>
</xsl:stylesheet>

when this transformation is run with Saxon 9.1.07, the correct output is written to the files:

C:\Program Files\Java\jre6\bin\temp\delete\destroyed.xml

and

C:\Program Files\Java\jre6\bin\temp\delete\non-destroyed.xml

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

7 Comments

Thank you for such a pretty solution.
for the XSL2.0 in the 'match=node...' part is it possible to generalize? right now I'm copying the whole part according to the node name. It still works but not pretty. Thank you.
@John: Sorry, I don't understand the question in your last comment. Could you, please, explain what you mean?
in the solution you use node in <xsl:template mode="destroyed" match="node[not(*/*/@action = 'destroy')]"/>. I was hoping to get a general solution as there is 'nodeA', nodeB, nodeC in the actual file. Thank you.
Oh, I see what you mean -- replace tha match pattern with: /*/*[not(*/*/@action[not(. = 'destroy')])] , etc. for all other patterns.
|
2

Which XSLT processor do you use? If that is an XSLT 2.0 processor like Saxon 9 or AltovaXML or XmlPrime then you can use the xsl:result-document instruction. With an XSLT 1.0 processor you will need to check whether the processor supports an extension instruction to create multiple result documents. So tell us which processor you use, then we can suggest concrete code samples for your input sample and the desired output samples.

[edit] I have prepared an XSLT 2.0 sample:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs"
  version="2.0">

  <xsl:param name="action" as="xs:string" select="'destroy'"/>

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

  <xsl:template match="/">
    <xsl:result-document href="node_{$action}.xml">
      <xsl:apply-templates/>
    </xsl:result-document>
    <xsl:result-document href="node_other_than_{$action}.xml">
      <xsl:apply-templates mode="other"/>
    </xsl:result-document>
  </xsl:template>

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

  <xsl:template match="/*/*[*/*[@action = $action]] 
                       | /*/*/*[*[@action = $action]]">
    <xsl:next-match/>
  </xsl:template>

  <xsl:template match="/*/*[not(*/*[@action = $action])] 
                       | /*/*/*[not(*[@action = $action])]
                       | /*/*/*/*[not(@action = $action)]"/>

  <xsl:template match="/*/*[*/*[not(@action = $action)]] 
                       | /*/*/*[*[not(@action = $action)]]" mode="other">
    <xsl:next-match/>
  </xsl:template>

  <xsl:template match="/*/*[not(*/*[not(@action = $action)])] 
                       | /*/*/*[not(*[not(@action = $action)])]
                       | /*/*/*/*[@action = $action]" mode="other"/>


</xsl:stylesheet>

1 Comment

Hi, I think I will use XSLT 1.0 but if it's too difficult ver 2 is also allright I have saxon9. Thanks a lot!

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.