我有一个XQuery函数可以将一组XML文件转换为HTML并压缩它们。它在每个文件上运行一个trasform来创建< entry>元件。
从该功能开始:
declare function xport:make-sources( $path as xs:string) as item()* {
for $article in collection(xmldb:encode-uri($path))
let $docnum := $article/article/div[@class = 'content']/@doc/string()
return
<entry name="{concat($docnum,'.html')}" type='text' method='store'>
{transform:transform($article, doc("/db/EIDO/data/edit/xsl/doc-html.xsl"), <parameters/>)}
</entry>
} ;
鉴于输入,我运行XQuery只是向我展示转换的结果......我看到了(正是我所期待的):
<entry name="LS01.html" type="text" method="store">
<html>
<head>
<style>
body {
font-family: Arial;
}
article img {
width:50%;
}
...
您将注意到此条目,并且所有条目都没有XML声明。
但现在让我们将它们放在一起并将这些条目发送到压缩。这都在Web应用程序中。完整的XQuery是这样的:
xquery version "3.0";
import module namespace transform = "http://exist-db.org/xquery/transform";
declare namespace xport = "http://www.xportability.com";
declare function xport:make-sources( $path as xs:string) as item()* {
for $article in collection(xmldb:encode-uri($path))
let $docnum := $article/article/div[@class = 'content']/@doc/string()
return
<entry name="{concat($docnum,'.html')}" type='text' method='store'>
{transform:transform($article, doc("/db/EIDO/data/edit/xsl/doc-html.xsl"), <parameters/>)}
</entry>
} ;
let $path := request:get-parameter("path", "")
let $filename := request:get-parameter("filename", "")
let $col := xport:make-sources($path)
return
response:stream-binary(
xs:base64Binary(compression:zip($col,true()) ),
'application/zip',
$filename
)
一切正常,我得到一个ZIP文件,其中包含从XML转换为HTML的所有文档。
但是,当我查看ZIP中的实际文件时,它有:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
XML声明不适用于ZIP的任何条目。它不存在于条目列表中的任何地方(因为它不可能)。但是,拉链它们的行动显然是在增加声明。我没有看到其他原因或方式。即使指定omit-xml-declaration或将XSL中的输出类型更改为text或HTML也没有区别。这当然是因为zip的条目列表如上所示,并且显示声明在转换之后不存在。
ZIP中的文件有一个附加的XML声明,句号。
是否有一些解决方法?
答案 0 :(得分:6)
当zip-bound <entry>
元素的内容传递给compression:zip()
函数时,会在查询中隐式引入XML声明。我建议使用fn:serialize()
函数显式设置序列化选项。以下示例代码显示了如何实现您描述的结果:
xquery version "3.1";
let $node := <html><head/><body><div><h1>Hello World!</h1></div></body></html>
let $serialized := serialize($node, map { "method": "xml", "indent": true(),
"omit-xml-declaration": true() })
let $entries := <entry name="test.html" type="text" method="store">{$serialized}</entry>
let $filename := "test.zip"
return
response:stream-binary(
compression:zip($entries, true()),
'application/zip',
$filename
)
将此查询保存到/db/apps/my-app/test.xq
这样的位置的数据库中并通过将您的网络浏览器指向http://localhost:8080/exist/apps/my-app/test.xq来调用它将导致您的浏览器下载test.zip
。打开此zip文件将显示缺少XML声明的test.html
文件:
<html>
<head/>
<body>
<div>
<h1>Hello World!</h1>
</div>
</body>
</html>
回到基础,XQuery中存在或不存在XML声明是通过omit-xml-declaration
serialization parameter来切换的。要为整个查询全局省略XML声明,可以将这组声明放在查询的序言中:
declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method "xml";
declare option output:omit-xml-declaration "yes";
或者,当在查询的一部分内进行本地序列化时,您可以将同一组参数作为映射传递给fn:serialize
函数(上面的代码示例中使用的方法):
fn:serialize($node, map { "method": "xml", "omit-xml-declaration": true() } )
(第二个选项参数还有一个XML语法。)
当前版本的eXist(v4.0.0)和最新版本(可能是v3.6.0左右)支持上述所有选项,所有版本都支持更紧凑eXist-specific serialization facility,使用{{ 1}}选项表示为由exist:serialize
对组成的字符串:
key=value
您可以在conf.xml configuration file中设置eXist的默认序列化行为。可以使用上述方法覆盖conf.xml中的默认值。 eXist中不同接口上的序列化行为(例如WebDAV或XML-RPC)通常遵循conf.xml中设置的默认值,但这些默认值可以基于每个接口覆盖;例如,请参阅有关eXist WebDAV interface的序列化文档。