0
votes
<name>
  <pattern>/Book/Title</pattern>
  <pattern>/Newspaper/Title</pattern>
</name>
<Description>
  <pattern>/Book/Descriptions/*</pattern>
  <pattern>/Newspaper/Descriptions/*<pattern>
</Description>

I have a collection of different XML files. Given a XML file like above, I'd like to extract semantically similar information from all of them and display it in a JSON format. E.g. I might want to extract name and description from one of the XMLs encoding book information to receive a file like:

 "name": "Harry Potter",
 "description": ["DescA", "DescB"]

The XML file belonging to that might look like:

<Book>
  <Title>Harry Potter</Title>
  <Author>J.K. Rowling </Author>
  <Description lang="de">DescA</Description>
  <Description lang="en">DescB</Description>
</Book>

I thought about using XSL 3.0 to use the xsl:evaluate function, but it is not working the way I expected.

With a code snippet like:

<xsl:variable name="pattern">
   <xsl:evaluate xpath="/Book/Descriptions/*" context-item="$root"/>
</xsl:variable>
<xsl:for-each select="$pattern">
    <xsl:value-of select="."/>
    <xsl:text>,</xsl:text>
</xsl:for-each>

I only get all Descriptions as one long concatenated string, instead of being able to loop through them. I would have expected an output like "DescA,DescB,", but only get "DescADescB,". I'm quite unfamiliar with XSL, so any help is appreciated. Be it in how to design the above described mapping in a more efficient way or how to use the evaluate function in such a way, that I can get the individual Descriptions. For information, currently using Saxon XSL HE 10.5 with Java.

2
Perhaps put the right as="item()*" on your xsl:variable, if you need a variable with xsl:evaluate. And for the whole question, a minimal but complete sample to reproduce things would help.Martin Honnen
Did the answers help?Martin Honnen

2 Answers

0
votes

Start simple with e.g.

  <xsl:template match="/">
    <xsl:value-of separator=",">
      <xsl:evaluate xpath="'Book/Description'" context-item="."/>
    </xsl:value-of>
  </xsl:template>

which, for your sample,

<Book>
  <Title>Harry Potter</Title>
  <Author>J.K. Rowling </Author>
  <Description lang="de">DescA</Description>
  <Description lang="en">DescB</Description>
</Book>

should give DescA,DescB.

Storing the sequence of items constructed inside of an xsl:variable element by e.g. xsl:evaluate as a sequence of items and not as child nodes of a result tree is achieved by the use of e.g. xsl:variable as="item()*" ..., not by changing your xsl:evaluate.

0
votes

I'm a little confused because you've got two XML documents here, both of which contain elements that match Description, but neither has anything that matches Book/Descriptions/*, and the one that contains patterns (expressions) to be evaluated doesn't contain a Book element.

Perhaps your real mistake is being masked by copy/paste errors. Your choice of variable name pattern also hints that you might be confused: the result of xsl:evaluate is not a pattern (or even an XPath expression), it is a set of nodes.

One definite error is that you didn't specify an as attribute on the variable (which means the results of xsl:evaluate are copied into a new document tree). I'd expect something like:

<xsl:variable name="selected-nodes" as="node()*">
   <xsl:evaluate xpath="//Description/pattern" context-item="$root"/>
</xsl:variable>

But I'm not sure that's exactly what you want, because //Description/pattern selects more than one expression, whereas xsl:evaluate will only evaluate one. If you want to evaluate multiple patterns and combine the results, that can be done, but I need to understand the requirements more clearly.