Как лучше всего определить все пространства имен для 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() сделай это.

Другие вопросы по тегам