How to handle multiple values returned by Saxon XQuery processor when one value is undefined?

65 Views Asked by At

I am trying to use the Saxon HE XQuery processor in a Java app to perform ad-hoc queries on XML documents.

For example:

for $x in //TestHistory[last()]/results/TestResult
where $x/status = 'TestedFailed'
return (/*/operatorId, 
        $x/status, 
        $x/expectedValue/rawValue, 
        $x/inputValue/rawValue)

Here I am gathering 4 values per hit.

The Java code is

Processor processor = new Processor(false);
XQueryCompiler compiler = processor.newXQueryCompiler();
XQueryExecutable exec = compiler.compile(query.getQuery());
XQueryEvaluator evaluator = exec.load();

XdmNode xmlDoc = processor.newDocumentBuilder().build(file);
evaluator.setSource(xmlDoc.asSource());

XdmValue eval = evaluator.evaluate();

int idx = 0;
int cols = 4;
int rows = eval.size/cols;
String[][] result = new String[rows][cols];

//foreach item in eval fill array

eval contains a simple sequence of XdmItems which I can process - assuming that there are 4 items per row.

However, if the node inputValue/rawValue has not been defined (which is sometimes the case) there is no corresponding XdmItem entry and my processing approach (4 items per row) no longer works.

How can I force each requested attribute to have a corresponding XdmItem in the result?

2

There are 2 best solutions below

0
Michael Kay On BEST ANSWER

The "," operator does sequence concatenation, which means empty sequences get lost: the result of ("a", (), "b") is ("a", "b").

One solution would be to return an array instead of a sequence:

return [/*/operatorId, 
        $x/status, 
        $x/expectedValue/rawValue, 
        $x/inputValue/rawValue]

You should then be able to cast the result in the Java code to XdmArray, and convert this to a Java list of length 4 using XdmArray.asList()

0
Martin Honnen On

Here is an example of returning a sequence of maps with four properties where the value of one property can be an empty sequence:

Input XML:

<root id="test-suite1">
  <item>
    <status>passed</status>
    <expected>a</expected>
    <returned>a</returned>
  </item>
  <item>
    <status>failed</status>
    <expected>d</expected>
    <returned>e</returned>
  </item>
 <item>
    <status>failed</status>
    <expected>f</expected>
  </item>
</root>

XQuery code:

declare namespace output = 'http://www.w3.org/2010/xslt-xquery-serialization';

declare option output:method 'adaptive';

for $item in root/item
return map {
  'id' : /root/@id => data(),
  'status' : $item/status => data(),
  'expected' : $item/expected => data(),
  'returned' : $item/returned => data()
}

Serialized result:

map{"returned":"a","status":"passed","expected":"a","id":"test-suite1"}
map{"returned":"e","status":"failed","expected":"d","id":"test-suite1"}
map{"returned":(),"status":"failed","expected":"f","id":"test-suite1"}

Online example.