eXist-db 转换中的特殊字符和 xmldb:store

eXist-db special characters in transformation and xmldb:store

我有一个关于 eXist-db 4.5 中输出转义的问题:

我正在使用 transform:transform$serialization-options = method=text media-type=application/text)和 xmldb:store$mime-type = text/plain)来保存XSL 转换的输出返回到数据库。在我的 xslt-Stylesheet 中,我正在使用

<xsl:value-of select="concat('Tom ', '&amp;', ' Peter')"/>

但是保存回 eXist 的输出看起来像 Tom $amp; Peter,而不是我预期的 Tom & Peter。 当我指定 disable-output-escaping="yes" 时,eXist 因错误而终止...

<xsl:value-of select="concat('Tom ', '&amp;', ' Peter')" disable-output-escaping="yes"/>

here 建议的那样使用 transform:stream-transform 也不起作用,因为我需要将输出保存到文本文件中。

如何确保我可以在我的 XSL 转换中使用 concat& 等特殊字符?


编辑:添加示例

假设您在 /db/apps/ 下有一个名为 temp 的 eXist 集合,其中包含以下文件:

input.xml

<?xml version="1.0" encoding="UTF-8"?>
<testxml>
    <name>Peter</name>
</testxml>

stylesheet.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="2.0">

    <xsl:template match="/">
    <!-- Ampersand is not encoded: --> <xsl:value-of select="concat('Tom ', '&amp; ', testxml/name)"/> -->
    <!-- transformation fails: <xsl:value-of disable-output-escaping="yes" select="concat('Tom ', '&amp;', testxml/name)"/> -->
    <!-- Doesn't work obviously: <xsl:value-of select="concat('Tom ', '&', testxml/name)"/> -->
    </xsl:template>

</xsl:stylesheet>

transformation.xq

xquery version "3.1";

declare function local:xml2tex() as xs:string
{
let $mime-type := "text/plain"
let $stylesheet := doc("/db/apps/temp/stylesheet.xsl")
let $serializationoptions := "method=text media-type=application/text"
let $doc := doc("/db/apps/temp/input.xml")
let $filename := (replace(util:document-name($doc), "\.xml$", "") || ".tex")
let $transform := transform:transform(
    $doc,
    $stylesheet,
    (),
    (),
    $serializationoptions)
let $store := xmldb:store("/db/apps/temp", $filename, $transform, $mime-type)
return
$filename
};

local:xml2tex()

当您使用包含的三个 value-of select 选项评估 transformation.xq 时,您会看到工作的那个生成一个 *.tex 文件,其内容 Tom &amp; Peter不是 的意图(即 Tom & Peter

根据 transform:transform() 的 eXist 函数文档,此函数 return 是一个 node()(或空序列)。因此,尽管您可能试图将 XSLT 转换的结果强制为一个普通的旧字符串(就像您通过提供 method=text 序列化参数所做的那样),该函数仍将 return 这作为节点的字符串 - 文本节点。

当您将文本节点传递给 xmldb:store() 函数以存储文本文件(在您的情况下为 .tex 文件)时,序列化再次发挥作用,因为文本节点必须序列化到eXist 用于文本文件的二进制形式。默认的序列化方法是 XML 方法,它在序列化文本节点时对字符串进行转义。

为了检验这个假设,运行 下面的查询并检查生成的文件:

xmldb:store("/db", "01-text-node.txt", text { "Tom &amp; Peter" } ),
xmldb:store("/db", "02-string.txt", "Tom &amp; Peter" )

为避免此问题并确保使用文本方法存储转换后的值,您应该使用几种派生文本节点字符串值的方法之一 - 这里我将这些方法应用于您的 $transform变量:

  1. 使用cast as运算符:$transform cast as xs:string
  2. 使用fn:string()函数:string($transform)$transform/string()
  3. 使用fn:serialize()函数:serialize($transform, map { "method": "text" } )

更新: 下面评论中报告的一个问题可能会导致 transform:transform() 函数 return 多于一个 node(),这可能导致上述解决方案 1 和 2 导致意外的基数错误。解决方法是使用 fn:string-join() 函数。解决方案 3 无需调整即可工作。