Java return 节点中的 Saxon 扩展函数

Saxon Extension function in Java return Node

我正在尝试使用此处定义的 Saxon 实现自定义函数-> https://specifications.xbrl.org/registries/functions-registry-1.0/80132%20xfi.identifier/80132%20xfi.identifier%20function.html

public class IdentifierFunction implements ExtensionFunction {

    public QName getName() {
        return new QName("http://www.xbrl.org/2005/function/instance", "identifier");
    }

    public SequenceType getResultType() {
        return SequenceType.makeSequenceType(ItemType.STRING, OccurrenceIndicator.ONE);
    }

    public net.sf.saxon.s9api.SequenceType[] getArgumentTypes() {
        return new SequenceType[] { SequenceType.makeSequenceType(ItemType.STRING, OccurrenceIndicator.ONE) };
    }

    public XdmValue call(XdmValue[] arguments) throws SaxonApiException {
        String arg = ((XdmAtomicValue) arguments[0].itemAt(0)).getStringValue();
        String newExpression="(//xbrli:xbrl/xbrli:context[@id=("+arg+"/@contextRef"+")])[1]/xbrli:entity/xbrli:identifier";
        String nodeString=this.getxPathResolver().resolveNode(this.getXbrl(),newExpression);
        return new XdmAtomicValue(nodeString);
    }
}

上面的resolveNode()代码实现如下

public String resolveNode(byte[] xbrlBytes, String expressionValue) {
        // 1. Instantiate an XPathFactory.
        javax.xml.xpath.XPathFactory factory = new XPathFactoryImpl();

        // 2. Use the XPathFactory to create a new XPath object
        javax.xml.xpath.XPath xpath = factory.newXPath();

        NamespaceContext ctx = new NamespaceContext() {
            @Override
            public String getNamespaceURI(String aPrefix) {
                if (aPrefix.equals("xfi"))
                    return "http://www.xbrl.org/2005/function/instance";
                else if (aPrefix.equals("xs"))
                    return "http://www.w3.org/2001/XMLSchema";
                else if (aPrefix.equals("xbrli"))
                    return "http://www.xbrl.org/2003/instance";
                else
                    return null;
            }

            @Override
            public Iterator getPrefixes(String val) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String getPrefix(String uri) {
                throw new UnsupportedOperationException();
            }
        };
        xpath.setNamespaceContext(ctx);
        try {
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
            Document someXML = documentBuilder.parse(new InputSource(new StringReader(new String(xbrlBytes))));

            // 3. Compile an XPath string into an XPathExpression
            javax.xml.xpath.XPathExpression expression = xpath.compile(expressionValue);
            Object result = expression.evaluate(someXML, XPathConstants.NODE);
            // 4. Evaluate the XPath expression on an input document
            Node nodes = (Node) result;
            return nodeToString(nodes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

当我评估 xfi:identifier(args) 时,我得到如下所示的字符串:

<xbrli:identifier xmlns:xbrli="http://www.xbrl.org/2003/instance"
                  xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
                  xmlns:jenv-bw2-dim="http://www.nltaxonomie.nl/nt13/jenv/20181212/dictionary/jenv-bw2-axes"
                  xmlns:jenv-bw2-dm="http://www.nltaxonomie.nl/nt13/jenv/20181212/dictionary/jenv-bw2-domains"
                  xmlns:jenv-bw2-i="http://www.nltaxonomie.nl/nt13/jenv/20181212/dictionary/jenv-bw2-data"
                  xmlns:kvk-i="http://www.nltaxonomie.nl/nt13/kvk/20181212/dictionary/kvk-data"
                  xmlns:link="http://www.xbrl.org/2003/linkbase"
                  xmlns:nl-cd="http://www.nltaxonomie.nl/nt13/sbr/20180301/dictionary/nl-common-data"
                  xmlns:rj-i="http://www.nltaxonomie.nl/nt13/rj/20181212/dictionary/rj-data"
                  xmlns:rj-t="http://www.nltaxonomie.nl/nt13/rj/20181212/dictionary/rj-tuples"
                  xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
                  xmlns:xlink="http://www.w3.org/1999/xlink"
                  scheme="http://www.kvk.nl/kvk-id">62394207</xbrli:identifier>

但是,我想计算函数 number(xfi:identifier(args))

这会导致 NaN,这是显而易见的,因为完整的节点字符串无法转换为数字。我想,我需要更改我的功能,以便它 returns 节点。但是,我不确定该怎么做。我尝试了 google 并查看了 Saxon 文档,但还没有成功。 有人能帮我吗?基本上,自定义函数应该根据定义 return 一个元素节点。当我使用 number(xfi:identifier) 它应该给我 62394207 在这种情况下。

问候,

文琪

首先,该函数的 XBRL 规范似乎暗示该函数需要一个节点作为参数,return一个节点作为其结果,但在您的实现中,getArgumentTypes() 和 getResultType() 定义了类型作为 xs:string - 所以这需要改变。

并且该函数应该 return 一个 XdmNode,它是 XdmValue 的子类。

接下来,每次执行您的函数时,创建 DocumentBuilderFactory 和 XPathFactory、构建 XML 文档树以及编译 XPath 表达式的效率非常低。我强烈怀疑 none 这是必要的。

与其将 this.getXbrl() return 原始词法文档作为 byte[],不如将其 return 表示文档树的预构建 XdmNode。然后我建议不要使用 XPath 在该树中进行选择,而是使用 Saxon 的类似 linq 的导航 API。如果此 XdmNode 在变量 "root" 中,则 XPath 表达式

//xbrli:xbrl/xbrli:context[@id=("+arg+"/@contextRef"+")

翻译成

root.select(descendant("xbrl").then(child("context)).where(attributeEq("id", arg))

(除了我不太确定您作为 arg 传递什么以使您的 XPath 表达式有意义)。

但如果您愿意,也可以使用 XPath;只需将 Saxon 的 s9api 接口用于 XPath,并确保 XPath 表达式仅编译一次并重复使用。然后直接得到 XdmNode 作为 XPath 表达式的结果,它可以作为扩展函数的结果直接 returned。