【问题标题】:Saxon Extension function in Java return NodeJava中的撒克逊扩展函数返回节点
【发布时间】:2019-10-05 11:53:28
【问题描述】:

我正在尝试使用此处定义的 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 很明显,因为完整的节点字符串无法转换为数字。我想,我需要改变我的函数,让它返回 Node.js。但是,我不知道该怎么做。我尝试了 google 并查看了 Saxon 文档,但还没有运气。 有人能帮我吗?基本上,自定义函数应该根据定义返回一个元素节点。当我使用 number(xfi:identifier) 在这种情况下它应该给我 62394207

问候,

文奇

【问题讨论】:

    标签: saxon


    【解决方案1】:

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

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

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

    不是让 this.getXbrl() 返回一个原始词汇文档作为 byte[],而是让它返回一个代表文档树的预构建 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 表达式的结果,它可以作为扩展函数的结果直接返回。

    【讨论】:

    • 我的问题在我使用 XPath 时得到了解决。但是,是否有使用 Saxon 的类似 linq 的导航 API 的良好文档,因为我想使用它。当我尝试导入我猜是静态的后代时出现错误。
    • 这个 API 的文档目前有点有限,但是在 saxon-resources 下载中有一些示例:查找 java/S9APIExamples.java,搜索 NavigationA、B、C、D。是的, Steps 和 Predicates 类中的方法应该是静态导入,例如`import static net.sf.saxon.s9api.streams.Predicates.*`。另请参阅 saxonica.com/documentation/index.html#!javadoc/… 的 Javadoc 和 dev.saxonica.com/blog/mike/2018/04/… 的博客
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-30
    • 2016-01-25
    • 1970-01-01
    • 2015-02-21
    • 2023-03-25
    • 2010-10-02
    相关资源
    最近更新 更多