Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT: Create node if it doesn't exist?

Tags:

xslt

How can I use XSLT to create nodes if they don't exist? I need to insert the node <sectionhead> under <group>, but if the <group> node doesn't exist, then I need to create that as well.

eg.

Input (group node exists):

<story>
    <group>
        <overhead>
            <l1>overhead</l1>
        </overhead>
        <headline>
            <l1>headline</l1>
        </headline>
    </group>
    <text>
        <lines>
            <l1>line</l1>
        </lines>
    </text>
</story>

Output:

<story>
    <group>
        <sectionhead />
        <overhead>
            <l1>overhead</l1>
        </overhead>
        <headline>
            <l1>headline</l1>
        </headline>
    </group>
    <text>
        <lines>
            <l1>line</l1>
        </lines>
    </text>
</story>

Input (group node does not exist):

<story>
    <text>
        <lines>
            <l1>line</l1>
        </lines>
    </text>
</story>

Output:

<story>
    <group>
        <sectionhead />
    </group>    
    <text>
        <lines>
                <l1>line</l1>
            </lines>
    </text>
</story>
like image 397
ilitirit Avatar asked Jun 13 '11 11:06

ilitirit


2 Answers

Try to translate the rules in your problem description directly into template rules:

"I need to insert the node <sectionhead> under <group>"

<xsl:template match="group">
  <group>
    <sectionhead/>
    <xsl:apply-templates/>
  </group>
</xsl:template>

"but if the <group> node doesn't exist, then I need to create that as well."

<xsl:template match="story[not(group)]">
  <story>
    <group>
      <sectionhead/>
    </group>
    <xsl:apply-templates/>
  </story>
</xsl:template>
like image 103
Michael Kay Avatar answered Oct 14 '22 02:10

Michael Kay


Here is a complete solution that overrides the identity rule/template for any story element that has no group child:

<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="story[not(group)]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
     <group>
       <sectionhead />
     </group>
   <xsl:apply-templates select="node()"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="group[not(sectionhead)]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
     <sectionhead />
   <xsl:apply-templates select="node()"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

when it is applied on the provided XML document (with no group):

<story>
    <text>
        <lines>
            <l1>line</l1>
        </lines>
    </text>
</story>

the wanted, correct result is produced:

<story>
   <group>
      <sectionhead/>
   </group>
   <text>
      <lines>
         <l1>line</l1>
      </lines>
   </text>
</story>

when applied to the first XML document (having group that has no sectionhead child):

<story>
    <group>
        <overhead>
            <l1>overhead</l1>
        </overhead>
        <headline>
            <l1>headline</l1>
        </headline>
    </group>
    <text>
        <lines>
            <l1>line</l1>
        </lines>
    </text>
</story>

this same transformation again produces the wanted, correct result:

<story>
   <group>
      <sectionhead/>
      <overhead>
         <l1>overhead</l1>
      </overhead>
      <headline>
         <l1>headline</l1>
      </headline>
   </group>
   <text>
      <lines>
         <l1>line</l1>
      </lines>
   </text>
</story>
like image 37
Dimitre Novatchev Avatar answered Oct 14 '22 02:10

Dimitre Novatchev