Halina Pichukha
Halina Pichukha

Reputation: 13

Select parent and child attribute value

I have an xml file like below:

<tag>
   <file name="name1">
      <error message="error1"/>
      <error message="error2"/>
   </file>
   <file name="name2">
      <error message="error1"/>
      <error message="error2"/>
   </file>
</tag>

I hope to get a next result:

name1: error1, error1;
name2: error1, error1

I'm trying to do something like this:

*//file/@name | //file/error/@message*

But this, of course, does not work. Can I implement a map?

Upvotes: 0

Views: 55

Answers (2)

Michael Kay
Michael Kay

Reputation: 163322

With XPath 3.1,

string-join(/tag/file ! (@name || ': ' || string-join(error/@message, ', ')), '#')

where # represents a newline character escaped according to the conventions of your host language, e.g. \n for Java, &#xa; for XSLT.

For XPath 2.0 replace A!B by for $a in A return B and replace A||B by concat(A, B)

XPath 1.0: not possible without host language support.

Upvotes: 1

zx485
zx485

Reputation: 29022

This is only possible with XPath-2.0 or above. You can use the following expression to create a string as a result. It uses the appropriate delimiters , and LF&#xa;.

for $nam in /tag/file/@name, $msg in $nam/../error return concat(if (generate-id($msg) = generate-id($nam/../error[1])) then concat($nam,': ',$msg/@message) else $msg/@message, if (generate-id($msg) = generate-id($nam/../error[last()])) then ';&#xa;' else ',')

The output is:

name1: error1, error2;
name2: error1, error2;

This output differs a little bit from your desired output above (error2 instead of error1) and a trailing ;. If you need to get rid of the last ;, you can do so by implementing the same principle like with the line-feeds.

P.S.: The approach for checking for the first and last elements seems to be sub-optimal, so there's probably room for improvement.

Upvotes: 0

Related Questions