Как лучше всего определить все пространства имен для Saxon XPathCompiler?
Когда мы загружаем XML-файл, если он использует пространства имен, мы должны передать все эти пространства имен компилятору.
Во-первых, существует ли какой-либо вызов, который сообщает компилятору, что он должен читать сам файл XML и использовать префиксы /URI, объявленные в файле? Мне всегда казалось странным, что я должен сказать компилятору то, что он может вычислить сам.
Но если это нужно сделать, то лучший ли способ сделать это?
Полный код для этого находится в SaxonQuestions.zip (без лицензионного ключа) - XmlDatasource.java.
public class XmlDatasource {
/** the DOM all searches are against */
private XdmNode xmlRootNode;
private XPathCompiler xPathCompiler;
/** key == the prefix; value == the uri mapped to that prefix */
private HashMap<String, String> prefixToUriMap = new HashMap<>();
/** key == the uri mapped to that prefix; value == the prefix */
private HashMap<String, String> uriToPrefixMap = new HashMap<>();
// .................
private void declareNameSpaces() throws SaxonApiException {
// saxon has some of their functions set up with this.
prefixToUriMap.put("saxon", "http://saxon.sf.net");
uriToPrefixMap.put("http://saxon.sf.net", "saxon");
XdmValue list = xPathCompiler.evaluate("//namespace::*", xmlRootNode);
if (list == null || list.size() == 0)
return;
for (int index=0; index<list.size(); index++) {
XdmNode node = (XdmNode) list.itemAt(index);
String prefix = node.getNodeName() == null ? "" : node.getNodeName().getLocalName();
// xml, xsd, & xsi are XML structure ones, not ones used in the XML
if (prefix.equals("xml") || prefix.equals("xsd") || prefix.equals("xsi"))
continue;
// use default prefix if prefix is empty.
if (prefix == null || prefix.isEmpty())
prefix = "def";
// this returns repeats, so if a repeat, go on to next.
if (prefixToUriMap.containsKey(prefix))
continue;
String uri = node.getStringValue();
if (uri != null && !uri.isEmpty()) {
xPathCompiler.declareNamespace(prefix, uri);
prefixToUriMap.put(prefix, uri);
uriToPrefixMap.put(uri, prefix); }
}
}
}
1 ответ
В общем, объявление пространств имен, присутствующих в исходном документе, не рекомендуется, потому что (а) различные привязки могут находиться в области видимости в разных частях исходного документа (по общему признанию, это наиболее вероятно для пространства имен по умолчанию) и (b) вы хотите, чтобы выражение XPath сохраняло свое значение, если автор документа решает использовать другие префиксы. Так что я бы не хотел "благословлять", чем практиковать, автоматизируя это.
Альтернатива вашему подходу (//namespace::*
) будет делать навигацию с Java: что-то вроде
xmlRootNode.select(descendant(isElement()).then(namespace())).forEach(...)
(Это предполагает статический импорт для Steps.*
а также Predicates.*
)
Я не знаю, есть ли польза от устранения дубликатов в коде сканирования или просто позволяя XPathCompiler.declareNamespace()
сделай это.