Reputation: 13
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
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, 

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
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

.
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 ';
' 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