To sort a query in Saxon we first run the query:
XPathExecutable exe = xPath.compile(query);
XPathSelector selector = exe.load();
selector.setContextItem(xmlDocument);
XdmValue nodeSet = selector.evaluate();
// put the results in an array
ArrayList<XdmItem> nodes = new ArrayList<XdmItem>();
for (int i = 0; i < nodeSet.size(); i++)
nodes.add(nodeSet.itemAt(i));
// Sort the results
sortNodes(nodes, "RiskLevel", false, false);
private void sortNodes(ArrayList<XdmItem> nodes, final String sortKey, final boolean sortIsAttr, boolean descending) {
Comparator comparator = new Comparator() {
public int compare(Object node1, Object node2) {
if (node1 instanceof XdmNode && node2 instanceof XdmNode) {
if (sortIsAttr) {
return ((XdmNode) node1).getAttributeValue(new QName(sortKey)).compareTo(((XdmNode) node2).getAttributeValue(new QName(sortKey)));
}
else {
XdmSequenceIterator iter1, iter2;
if (sortKey.equals(".")) {
iter1 = ((XdmNode) node1).axisIterator(Axis.SELF, new QName(((XdmNode) node1).getNodeName().getLocalName()));
iter2 = ((XdmNode) node2).axisIterator(Axis.SELF, new QName(((XdmNode) node2).getNodeName().getLocalName()));
} else if (sortKey.contains("/")){
// we get here when the sortKey is a descendant, but not direct child of the node, so we traverse down the tree to get there
String key = sortKey;
while (key.contains("/")) {
node1 = ((XdmNode) node1).axisIterator(Axis.CHILD, new QName(key.substring(0, key.indexOf("/")))).next();
node2 = ((XdmNode) node2).axisIterator(Axis.CHILD, new QName(key.substring(0, key.indexOf("/")))).next();
key = key.substring(key.indexOf("/") + 1);
}
iter1 = ((XdmNode) node1).axisIterator(Axis.CHILD, new QName(key));
iter2 = ((XdmNode) node2).axisIterator(Axis.CHILD, new QName(key));
} else {
iter1 = ((XdmNode) node1).axisIterator(Axis.CHILD, new QName(sortKey));
iter2 = ((XdmNode) node2).axisIterator(Axis.CHILD, new QName(sortKey));
}
if(iter1.hasNext() && iter2.hasNext()) {
String val1 = iter1.next().getStringValue();
String val2 = iter2.next().getStringValue();
if(parseableAsDouble(val1) && parseableAsDouble(val2)) {
Double val1Double = Double.parseDouble(val1);
Double val2Double = Double.parseDouble(val2);
return val1Double.compareTo(val2Double);
}
Date val1Date = parseAsDate(val1);
Date val2Date = parseAsDate(val2);
if(val1Date != null && val2 != null)
return val1Date.compareTo(val2Date);
return (val1.compareTo(val2));
}
return 0;
}
} else {
assert node1 != null && node2 != null;
return ((XdmItem)node1).getStringValue().compareTo(((XdmItem) node2).getStringValue());
}
}
};
if (descending)
comparator = Collections.reverseOrder(comparator);
Collections.sort(nodes, comparator);
}
The problem is for the following XML (nodeSet.toString():
<Securities xmlns="http://www.windward.net">
<RiskLevel>4</RiskLevel>
The code:
iter1 = ((XdmNode) node1).axisIterator(Axis.CHILD, new QName(sortKey));
iter2 = ((XdmNode) node2).axisIterator(Axis.CHILD, new QName(sortKey));
}
if(iter1.hasNext() && iter2.hasNext()) {
returns false on both hasNext() calls.
First question, is this the beast way to do this? If not, what is a better approach?
Second question, if this is the best way, why do the iterators return false ofr hasNext()?
Why don't you use XQuery to do the sorting? Generate and compile the query
'declare variable $nodes external;
for $n in $nodes order by $n/' + sortkey + ' return $n'
and then compile and execute this query, binding $nodes
to your nodeSet
obtained from the first query.
I would think that the reason your code is failing is that the elements are in a namespace, and when you construct a QName to hold the sort key, you are constructing a no-namespace QName.
来源:https://stackoverflow.com/questions/54738705/trying-to-sort-a-node-set-from-saxon