【问题标题】:Is it possible to cache XML documents in Saxon to avoid re-parsing and re-indexing?是否可以在 Saxon 中缓存 XML 文档以避免重新解析和重新索引?
【发布时间】:2020-01-14 05:03:35
【问题描述】:

我目前正在评估带有 Saxon 的 XSLT3 是否对我们的目的有用。请听我说完。

我们正在开发一个 REST API,它在给定输入请求 XML 的情况下提供凭据。基本上,有 3 个文件在播放:

  • site.xml:
    • 此文件包含代表整个组织的数据:用户、角色、凭据、设置...
    • 它可以轻松包含 10.000 行。
    • 它可以被认为是静态的/不可变的。
    • 您可以将其与数据库的 XML 表示进行比较,可以这么说。
  • request.xml:
    • 此文件包含提供给 REST API 的请求。
    • 相当小,通常在 10 到 50 行左右。
    • 每个请求都不同。
  • request.xslt:
    • 此文件包含用于将给定 request.xml 转换为输出 XML 的样式表。
    • 它通过 XSLT document() 函数加载 site.xml,因为它需要这些数据来满足请求。

这里的问题是在request.xslt 中加载site.xml 需要很长时间。此外,对于每个请求,必须重建 XSLT <xsl:key .../> 指令引入的索引。这加起来了。

因此以某种方式缓存site.xml 是有意义的,以避免为每个请求解析和索引该文件。

请务必注意,多个 API 请求可以同时发生,因此在多个正在进行的 XSLT 转换之间共享这个缓存的 site.xml 应该是安全的。

Saxon (Java) 可以做到这一点吗?这将如何运作?

更新 1

经过一些额外的思考,我意识到也许我不应该只缓存site.xml XML 文件,而是缓存request.xslt?这假定通过document() 加载到request.xslt 中的site.xml 是该缓存的一部分。

【问题讨论】:

    标签: java xml xslt saxon xslt-3.0


    【解决方案1】:

    嗯,这当然是可能的,但最好的方法在一定程度上取决于具体情况,例如当 site.xml 更改时会发生什么。

    我倾向于在应用程序启动时创建单个 s9api 处理器,并立即(即在应用程序初始化期间)使用 Processor.DocumentBuilder.build() 将 site.xml 加载到 XdmNode 中;然后可以将其作为参数值(<xsl:param>)传递到使用它的每个转换中。或者,如果您更喜欢使用document() 访问它,您可以注册一个URIResolver,通过返回相关的XdmNode 来响应document() 调用。

    关于索引和key()函数,只要xsl:key定义是“可共享的”,那么如果基于同一个编译样式表(s9apiXsltExecutable)的两个转换访问同一个文档,索引将不能重建。如果xsl:key 定义的matchuse 属性不依赖于从一种转换到另一种转换的任何内容,例如全局变量或参数的内容,则xsl:key 定义是可共享的。

    Saxon 的原生树实现(与 DOM 不同)是线程安全的:如果您构建一个文档一次,就可以在多个线程中访问它。支持 key() 函数的索引构建是同步的,因此并发转换不会相互干扰。

    Martin 允许对 document() 调用进行编译时评估的建议也可行。您还可以将文档放入使用 static="yes" 定义的全局变量中。但是,这在将编译后的样式表导出到持久文件时效果不佳:导出包含节点值静态变量的样式表时存在一些限制。

    【讨论】:

    • 感谢 Michael 的见解,这些非常有用。除了 Martin 的建议,我一定会尝试将 site.xml 解析为 XdmNode 并将其传递给转换。这看起来很有希望,可能是我一直在关注的。很高兴听到这里介绍了线程安全。
    • 两个答案都很棒,并且包含的​​知识比我希望的要多得多。我随机选择了一个作为接受的答案,但我很想同时接受。非常感谢。
    【解决方案2】:

    如果您向我们展示/告诉我们您使用哪个 API 来运行带有 Saxon 的 XSLT,将会有所帮助。

    至于缓存 XSLT,使用 JAXP,我认为您可以使用 newTemplatesTransformerFactoryImpl (http://saxonica.com/html/documentation/using-xsl/embedding/jaxp-transformation.html) 创建的 Templates TransformernewTransformer()

    使用 s9api API,您可以编译一次以获得“不可变的,因此是线程安全的”XsltExecutable (http://saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/XsltExecutable.html),然后您必须向我们 load()load30() 创建一个 @ 987654333@ 或 Xslt30Transformer 每次需要运行代码时。

    关于共享文档,见http://saxonica.com/html/documentation/sourcedocs/preloading.html

    有一个选项可用 (Feature.PRE_EVALUATE_DOC_FUNCTION) 来指示 使用常量字符串调用 doc() 或 document() 函数 编译查询或样式表时应评估参数, 而不是在运行时。此选项旨在用于当 所有查询都使用参考或查找文档,并且 转换

    然而,关于该配置选项的部分指出:

    在 XSLT 3.0 中,一种更好的方法是预先加载外部文档 样式表编译时是使用静态全局的新工具 变量。

    所以在这种情况下你可以声明

    <xsl:variable name="site-doc" static="yes" select="doc('site.xml')"/>
    

    您需要等待 Michael Kay 的回复,看看这是否足以共享文档。

    【讨论】:

    • 感谢 Martin 的见解,这些非常有用。我还没有写任何有用的代码,所以我没有展示任何代码。我肯定会尝试启用PRE_EVALUATE_DOC_FUNCTION 功能的Xslt30Transformer。这听起来正是我需要的。
    • 两个答案都很棒,并且包含的​​知识比我希望的要多得多。我随机选择了一个作为接受的答案,但我很想同时接受。非常感谢。
    • 需要注意的一点是 PRE_EVALUATE_DOC_FUNCTION 是不分青红皂白的:如果您的样式表中同时具有 doc('site.xml') 和 doc('request.xml')`,那么它将同时适用于两者。
    • 对!谢谢你的提醒。我的想法是通过doc() 导入site.xml(同时依赖Feature.PRE_EVALUATE_DOC_FUNCTION 的缓存功能)并将request.xml 提供给Xslt30Transformer.transform() 函数,如saxonica.com/html/documentation/using-xsl/embedding/… 中所述。
    猜你喜欢
    • 2018-03-10
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    • 2016-11-09
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    相关资源
    最近更新 更多