0

Yesterday I asked a question about loading external XMLs dynamically from XSLTs. It was answered, but now I have another problem. If you read that question you'll see an <include ref="/path/to/some.xml" /> at the third level. What if the level at which the <include> occurs varies?

Is there a way to dynamically choose to extract the nodes from that XML on the same level on which they occur in the original file?

EDIT: To be clear: I'm talking about the "element path" in the XML (the position of the include element in the XML), not the file path.

merge.xslt:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

  <xsl:output indent="yes" method="xml" encoding="utf-8"
    omit-xml-declaration="yes" />

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

  <xsl:template match="include">
      <!-- the depth of the extraction path (fruit/*/*/*... etc) should
           be based on the position of the matched <include> element -->
      <xsl:apply-templates select="document(@ref)/fruit/*/*/*"/>
  </xsl:template>
</xsl:stylesheet>

fruit.xml:

<fruit>
  <local>
    <apples>
      <include ref="apples.xml" />
    </apples>
  </local>
  <exotic>
    <bananas>
      <deeperlevel>
        <include ref="bananas.xml" />
      </deeperlevel>
    </bananas>
    <oranges>
      <include ref="oranges.xml" />
    </oranges>
  </exotic>
</fruit>

bananans.xml:

<fruit>
  <exotic>
    <bananas>
      <deeperlevel>
        <banana type="chiquita" color="yellow" />
        <banana type="generic" color="yellow" />
      </deeperlevel>
    </bananas>
  </exotic>
</fruit>

result:

<fruit>
  <local>
    <apples>
      <apple type="jonagold" color="red"/><apple type="granny-smith" color="green"/>
    </apples>
  </local>
  <exotic>
    <bananas>
      <deeperlevel>
        <deeperlevel> <!-- I don't want the second <deeperlevel> here, instead
                           <bananas .../> should be extracted, right after the
                           first <deeperlevel> -->
          <banana type="chiquita" color="yellow"/>
          <banana type="generic" color="yellow"/>
        </deeperlevel>
      </deeperlevel>
    </bananas>
    <oranges>
      <orange type="juice-orange" color="orange"/><orange type="red-orange" color="red"/>
    </oranges>
  </exotic>
</fruit>
Community
  • 1
  • 1
pancake
  • 1,923
  • 2
  • 21
  • 42
  • Are you talking about the number of steps in the `ref` attribute value (i.e. `/path/to/some.xml`) when referring to levels? Or are you talking about the position of `include` elements? In the latter case the suggested solution should work, it simply matches any `include` element, independent of the nesting level it occurs in. You might want to post a new XML input sample and the result you want to explain to us which additional requirements you face. – Martin Honnen May 22 '13 at 10:22
  • I edited my question, it's about the selection of the element that is extracted from the external XML document, they already end up at the correct position in the output XML. – pancake May 22 '13 at 11:08

1 Answers1

0

I've found a solution to my problem. It's not extremely nice IMHO, because it's an "eval"-type solution, but it works. I applied the 3rd suggestion in the answer of this question to my problem, the dynamic XPath evaluation extension (dyn:evaluate()). Luckily the xsltproc tool from the libxslt library (installed through macports, version 1.1.27) supports it.

I am going to leave the question open for a while, maybe someone has a better solution.

XSLT:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:dyn="http://exslt.org/dynamic"
  extension-element-prefixes="dyn">

  <xsl:output indent="yes" method="xml" encoding="utf-8"
    omit-xml-declaration="yes" />

  <xsl:template match="@*|node()">
    <xsl:param name="depth" select="'/*'" />
    <xsl:copy>
      <xsl:apply-templates select="@*|node()">
        <xsl:with-param name="depth" select="concat($depth, '/*')" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="include">
    <xsl:param name="depth" />
    <xsl:apply-templates select="dyn:evaluate(concat('document(@ref)', $depth))">
      <xsl:with-param name="depth" select="$depth" />
    </xsl:apply-templates>
  </xsl:template>

</xsl:stylesheet>
Community
  • 1
  • 1
pancake
  • 1,923
  • 2
  • 21
  • 42