Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

XSL: Match everything except... 1

Status
Not open for further replies.

csteinhilber

Programmer
Aug 2, 2002
1,291
US
I'm feeling a little stupid at the moment because I'm spinning my wheels trying to figure out what I think should be a pretty simple XSLT concept.

I have one XML file, that I want to combine into another XML file, based on the location of a specific node/element... such that the include XML might look something like:

Code:
<?xml version='1.0'?>
<root>
   <someElem>
      <someChild>
         Value
      </someChild>
      <someChild>
         <someGrandChild>Value</someGrandChild>
      </someChild>
      <someOtherChild>
         Value
      </someOtherChild>
   </someElem>
</root>

and then the base XML looks something like:
Code:
<?xml version='1.0'?>
<root>
   <someElem>
      <someChild>
         <someGrandChild>Value</someGrandChild>
      </someChild>
      <someChild>
         Value
      </someChild>
      <includeOtherFileHere>
         <fileToInclude>/path/to/file.xml</fileToInclude>
      </includeOtherFileHere>
      <someOtherChild>
         Value
      </someOtherChild>
      <stillSomeOtherChild>
         Value
      </stillSomeOtherChild>
   </someElem>
</root>

and when the XSL matches //includeOtherFileHere, I want to do something like:

Code:
<xsl:template match="//includeOtherFileHere">
    <!-- TODO get actual value of file path attribute -->
    <xsl:value-of select="document('/path/to/file.xml')" />
</xsl:template>

but all other elements, I simply want to copy verbatim... so the resulting XML would look like:
Code:
<?xml version='1.0'?>
<root>
   <someElem>
      <someChild>
         <someGrandChild>Value</someGrandChild>
      </someChild>
      <someChild>
         Value
      </someChild>
   <someElem>
      <someChild>
         Value
      </someChild>
      <someChild>
         <someGrandChild>Value</someGrandChild>
      </someChild>
      <someOtherChild>
         Value
      </someOtherChild>
   </someElem>
      <someOtherChild>
         Value
      </someOtherChild>
      <stillSomeOtherChild>
         Value
      </stillSomeOtherChild>
   </someElem>
</root>

the problem being... I don't necessarily know the names of the other elements (I may need to match //someOtherChild, or I may need to match //stillSomeOtherChild, or I may need to match some as-yet-unspec'd node name).

What's the most efficient way to perform the value-of of an external document if I match //includeOtherFileHere, otherwise copy the node (and any children) exactly?

It just isn't clicking for me today.

Thanks in advance!
-Carl


 
Not sure I get the essence of the question. Maybe the <root> in the external are concerned? In that case, you can do this.
[tt]
<xsl:template match="includeOtherFileHere">
<!-- TODO get actual value of file path attribute -->
<xsl:[blue]copy-of[/blue] select="[blue]document(./*[1]/text())/*[1]/*[/blue]" />
</xsl:template>
[/tt]
If you have only one <fileToInclude> inside the <includeOtherFileHere>, then a shortcut may be like this.
[tt]
<xsl:template match="includeOtherFileHere">
<!-- TODO get actual value of file path attribute -->
<xsl:[blue]copy-of[/blue] select="[blue]document(.)/*[1]/*[/blue]" />
</xsl:template>
[/tt]

 
Thanks, tsuji... that was certainly another hurdle I was facing, and your answer will undoubtedly help.

But the essence of my question, I guess, revolves more around how to match "anything else", if I'm already implicitly matching a node.

I don't know if it's a "feature" unique to my XSL transform engine (ColdFusion) or not (I'm thinking not)... but if I do not specifically select in templates that match all the elements in an XML document, the XmlText of the remaining elements are output in clear text.

In other words, if I had an XML file like:
Code:
<root>
   <matchedElem>
        <col>Row 1 Column 1</col>
        <col>Row 1 Column 2</col>
        <col>Row 1 Column 3</col>
   </matchedElem>
   <matchedElem>
        <col>Row 2 Column 1</col>
        <col>Row 2 Column 2</col>
        <col>Row 2 Column 3</col>
   </matchedElem>
   <unmatchedElem>
        <col>Row 3 Column 1</col>
        <col>Row 3 Column 2</col>
        <col>Row 3 Column 3</col>
   </unmatchedElem>
</root>

and my XSL looked like (pseudo code):
Code:
<?xml ...>
<xsl:stylesheet ...>

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

<xsl:template match="/matchedElem">
    <tr>
    <xsl:apply-templates select="./*" />
    </tr>
</xsl:template>

<xsl:template match="/col">
    <td>
    <xsl:value-of select="." />
    </td>
</xsl:template>
</xsl:stylesheet>

Because I don't have an xsl:template that specifically matches "unmatchedElem", I get output that looks like:

Code:
<table>
   <tr>
       <td>Row 1 Column 1</td>
       <td>Row 1 Column 2</td>
       <td>Row 1 Column 3</td>
   </tr>
   <tr>
       <td>Row 2 Column 1</td>
       <td>Row 2 Column 2</td>
       <td>Row 2 Column 3</td>
   </tr>
</table>

Row 3 Column 1
Row 3 Column 2
Row 3 Column 3
where the XmlText of the unmatched tags is just surreptitiously output at the end of my presentation.

In the past, I solved this by having xsl:templates that matched every possible element name in the document. This seemed like good practice, anyway... since XML is supposed to validate against a DTD, so I really should know all possible elements/nodes that my XSL would ever have to evaluate.

But in this case, because of various issues, this particular XML doc is not going to validate against a DTD, and the spec is in flux... so I'm looking for a way to match elements generically... except for this one particular includeOtherFileHere element.

So I'm looking for xsl that would essentially perform:
Code:
<xsl:template match="if not /includeOtherFileHere">
    <!-- do my generic transform -->
</xsl:template>

<xsl:template match="/includeOtherFileHere">
    <!-- otherwise, do my transform specific to this node -->
</xsl:template>

... ooooorrrrr... maybe I'm missing a fundamental concept with XSL, here, that's not letting me see the light. I'm the first to admit that that's entirely possible. I've produced a number of pretty complex XSLTs... but I'm entirely self-taught... so they probably aren't always the most efficient route to get to where I want to be.

Any insight would be greatly appreciated.

-Carl
 
Have read a couple of times your followup, Carl, hard to figure out the link with the first post. I would read again... If I pick up the unwanted text in the presentation, it is not an uncommon occurence. You can device and add a low priority template.
[tt]
<xsl:template match="text()" />
[/tt]
It would set it straight to their place.
 
I would match "node()", which will match all nodes, then match the "includeOtherFileHere" node in a seperate template. When you specify a node name, this has greater precedence than node(). For your example, the XSL would look like this:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">
    <table>
      <xsl:apply-templates select="*/*"/>
    </table>
  </xsl:template>
  <xsl:template match="node()">
    <tr>
      <xsl:apply-templates select="*"/>
    </tr>
  </xsl:template>
  <xsl:template match="unmatchedElem">
    <tr style="color: blue;">
      <xsl:apply-templates select="*"/>
    </tr>
  </xsl:template>
  <xsl:template match="col">
    <td>
      <xsl:value-of select="."/>
    </td>
  </xsl:template>
</xsl:stylesheet>

Jon

"I don't regret this, but I both rue and lament it.
 
JontyMC said:
When you specify a node name, this has greater precedence than node().

Thank you, Jon. That's the piece of information I needed! Is there some online reference that defines/describes the precedences that XSL follows? Or do you just discover that an implicit node name takes precedence over "node()" simply through trial and error.

Tsuji, sorry my follow up wasn't clear. I went off on sort of a tangent... but it is related, really. My point was I was trying to match every element generically, except for the one specific element I needed to do the document include on. When you said you didn't understand the problem, I retreated to a higher level view to explain that the issue was I couldn't seem to match elements generically when I also needed to match that one specific element (okay... it really wasn't much of a tangent at all... it's the exact same problem).

Thanks for the insight!
-Carl
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top