This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Merge XML, simple but can't get it!


Hi Jason,

> The XSL below is supposed to process the xml by merging the
> information in <Returned> back in main body, not sure if we are
> using the right methodology as we are still new to XML, if anyone
> can point me in the right direction it would be much appreciated!

You seem to be on the right track, using something based on the
identity template to work through the main body, and going off to find
the relevant part of the Returned information as required.

There are a few things that you could do to improve it, and a couple
of things that you're doing that are a bit weird so it's probably
worth checking whether you mean them or not, but you haven't said what
part isn't working or what you want it to come out with.

Perhaps the problem is the fact that the content of the Returned
element is getting output when you don't want it to be?  You can solve
that by only applying templates to the Policy within a
MergePolicy-matching template:

<xsl:template match="MergePolicy">
   <xsl:apply-templates select="Policy" />
</xsl:template>

Or by having an empty template matching the Returned element:

<xsl:template match="Returned" />

You could make it more efficient if you had a global variable that
held the Returned information so you don't have to go and get it every
time you come to another node:

<xsl:variable name="returned" select="/MergePolicy/Returned" />

You could also use intermediate templates within your identity
template, again to save repeating the same paths:

<xsl:template match="@*|node()" mode="copy">
  <xsl:copy>
    <xsl:apply-templates select="@*" mode="copy" />
    <xsl:variable name="currentNode" select="name()" />
    <xsl:variable name="descendantNode" select="name(descendant::*)" />
    <xsl:variable name="currentRefAttribute" select="@ref" />
    <xsl:variable name="returnedNode"
                  select="$returned/*[name() = $currentNode and
                                      @ref = $currentRefAttribute]" />
    <xsl:choose>
      <xsl:when test="$returnedNode/*[name() = $descendantNode]">
        <xsl:apply-templates select="$returnedNode" mode="add" />
      </xsl:when>
      <xsl:when test="$returnedNode">
        <xsl:copy-of select="$returnedNode/*" />
        <xsl:apply-templates mode="copy" />
      </xsl:when>
      <xsl:otherwise>
         <xsl:if test="$returnedNode">
         </xsl:if>
         <xsl:apply-templates mode="copy" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:copy>
</xsl:template>

The problem that leaps out with the above is the definition of the
$descendantNode variable.  The name() function will get the name of
the first node in the node set you pass to it as an argument.  The
first descendant of an element is the same as its first child, so the
definition you have above is equivalent to:

  <xsl:variable name="firstChildNode" select="name(*[1])" />

In fact, the current behaviour of the template means that you could
change the template to the following with the same effect:

<xsl:template match="@*|node()" mode="copy">
  <xsl:copy>
    <xsl:apply-templates select="@*" mode="copy" />
    <xsl:variable name="currentNode" select="name()" />
    <xsl:variable name="firstChildNode" select="name(*[1])" />
    <xsl:variable name="currentRefAttribute" select="@ref" />
    <xsl:variable name="returnedNode"
                  select="$returned/*[name() = $currentNode and
                                      @ref = $currentRefAttribute]" />

    <!-- if there's a node in Returned that matches, then copy its
         children -->
    <xsl:if test="$returnedNode">
       <xsl:copy-of select="$returnedNode/*" />
    </xsl:if>
    
    <!-- if the node in Returned doesn't have a child whose name is
         the same as the first child of the current node, then carry
         on processing the children of the current node -->
    <xsl:if test="not($returnedNode/*[name() = $firstChildNode])">
       <xsl:apply-templates mode="copy" />
    </xsl:if>
  </xsl:copy>
</xsl:template>

If you have lots of elements underneath Returned, you might consider
having a key:

<xsl:key name="returnedNodes" select="Returned/*"
         use="concat(name(), ':', @ref)" />

You could then retrieve the returned Node with the following instead:

  <xsl:variable name="returnedNode"
      select="key('returnedNodes', concat(name(), ':', @ref))" />

and wouldn't have to use $returned, $currentNode or
$currentRefAttribute.
      
I hope that helps,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/



 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]