Use/include a value from another node

358 views Asked by At

I have an XML file, like this:

<area1>
    <settings>
        <setting name="to_include">value</setting>
    </settings>
</area1>
<area2>
    <settings>
        <setting name="where_to_include">?????</setting>
    </settings>
</area2>

I want to include the value of the setting node "to_include" as value of setting node "where_to_include". How could I do it, in a pure XML way? XInclude? XPointer? XPath? XLink? Other?

I hoped I could do something like this:

<area2>
    <settings>
        <setting name="where_to_include">
            <xi:include href="SAMEFILE" xpointer="xpointer(/areas/area1/settings/setting[@name='debug'])">
                <xi:fallback>FALLBACK VALUE</xi:fallback>
        </setting>
    </settings>
</area2>

But with XInclude I cannot refer to the document itself while parsing it.

1

There are 1 answers

1
Jesse Alama On

I don't quite understand the question, but I think I have an idea what you mean. This may not be what you're after. You mention XPointer and XInclude, which seem like overkill to me.

Here's an XSLT (1.0) solution. The stylesheet takes an XML file like the one you give in the question and plugs value in for ?????. (Your XML is, by the way, missing a wrapper element, so it is technically malformed. But I think I understand what you mean. I'll silently add areas as a document element, and that's how the stylesheet that follows works. You may need to tweak it to fit the XML you're actually working with.)

Here is the stylesheet:

<?xml version='1.0' encoding='UTF-8'?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="*">
    <xsl:variable name="n" select="name (.)"/>
    <xsl:element name="{$n}">
      <xsl:for-each select="@*">
        <xsl:copy-of select="."/>
      </xsl:for-each>
      <xsl:apply-templates select="node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:copy-of select="."/>
  </xsl:template>

  <xsl:template match="/">
    <xsl:element name="areas">
      <xsl:apply-templates select="areas"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="areas">
    <xsl:apply-templates select="area1"/>
    <xsl:apply-templates select="area2"/>
  </xsl:template>

  <xsl:template match="text()[parent::setting[ancestor::area2]]">
    <xsl:apply-templates select="preceding::area1/settings/setting/text()"/>
  </xsl:template>
</xsl:stylesheet>

The stylesheet essentially just copies its input XML document, except at one point: when dealing with the setting under area2, it doesn't copy the text but goes back to the area1 node and copies the text under its setting. You can confirm that this works using any XSLT processor, e.g., xsltproc (it sounds like you have some non-trivial XML functionality at your disposal, so this should be pretty straightforward). Just make some changes to value and run the stylesheet on the changed XML document.