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。
我正在尝试使用此处定义的 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。