tags:

views:

345

answers:

3

What I tried to say in the title is:

Given a bit of XML in which I know a particular element appears only once, is it possible using a single XPath query to select a node-set that contains that element twice?

I understand there's a "union" operator (|) but that's basically a logical-OR, right? In SQL terms I'm looking then for the equivalent of a "union all".

E.g. Given the XML fragment...

<toplevel>
  <ElementIWant>
    <SomeSubElement1>specific data</SomeSubElement1>
    <SomeSubElement2>specific data 2</SomeSubElement2>
  </ElementIWant>
</toplevel>

...is there a query that will get me a result set equivalent to...

<ElementIWant>
  ...identical content...
</ElementIWant>
<ElementIWant>
  ...identical content...
</ElementIWant>

I haven't found anything that makes me think it can be done - but that's why I'm asking...

+2  A: 

XPath gives you node-sets, so by definition nodes appear only once. Now, you can have named template and call it twice with same XPath.

<xsl:template match="/ElementIWant"> 
  <xsl:call-template name="repeat"/>
  <xsl:call-template name="repeat"/>
</xsl:template>

<xsl:template name="repeat"> 
  <xsl:copy select=".">
    <xsl:text>... same content ...</xsl:text>
  </xsl:copy>
</xsl:template>
vartec
well, that's the only way to do it ;-)
vartec
Cheers (both). Next time I'll try to remember that, just sometimes, the terminology is there to help us!
mwardm
A: 

XPath is a language to query the data within an XML document while the SQL UNION ALL syntax combines resultsets from two different queries.

XPath by itself cannot be used to present the existing data in a format that does not match the existing format. However, you could use XSLT to perform this transformation:

<x:stylesheet version="1.0" xmlns:x="http://www.w3.org/1999/XSL/Transform"&gt;
  <x:output method="xml"/>

  <x:template match="/">
    <topMostLevel>
      <x:apply-templates />
    </topMostLevel>
  </x:template>

  <x:template match="toplevel">
    <x:copy-of select="."/>
    <x:copy-of select="."/>
  </x:template>
</x:stylesheet>
Cerebrus
+2  A: 

As pointed in the other answers, XPath cannot modify an XML document and produce new nodes.

Any node can participate in a node-set only once, due to the definition of "set".

However, XPath 2.0 gives us the new sequence type, which allows items to be repeated.

In order to have an element appearing twice in a sequence, one would just use the sequence concatenation operator "," as in the below expression:

/*/ElementYouWant, /*/ElementYouWant

Put this into an XSLT2.0 stylesheet as simple as this:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:f="http://fxsl.sf.net/"
 exclude-result-prefixes="f xs"
 >

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
   <t>
     <xsl:sequence select=
      "/*/ElementYouWant, /*/ElementYouWant"/>
   </t>
 </xsl:template>
</xsl:stylesheet>

And apply this transformation on this XML document:

<toplevel>
    <ElementYouWant>
     <SomeSubElement1>specific data</SomeSubElement1>
     <SomeSubElement2>specific data 2</SomeSubElement2>
    </ElementYouWant>
</toplevel>

to produce the wanted result:

<t>
   <ElementYouWant>
         <SomeSubElement1>specific data</SomeSubElement1>
         <SomeSubElement2>specific data 2</SomeSubElement2>
      </ElementYouWant>
   <ElementYouWant>
         <SomeSubElement1>specific data</SomeSubElement1>
         <SomeSubElement2>specific data 2</SomeSubElement2>
      </ElementYouWant>
</t>

Do note, that if one uses the <xsl:sequence> instruction, no new copy of the <ElementYouWant> element is created -- therefore in XSLT 2.0 it is recommended to use <xsl:sequence> and to avoid using <xsl:copy-of> which creates (unnecessary) copies of nodes.

Dimitre Novatchev
Dam,I am going to have to learn xpath all over again. :) thanks for the heads-up.
paulmurray