eXist-db压缩:zip函数是否添加XML声明

时间:2018-03-15 00:35:18

标签: xquery exist-db

我有一个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声明,句号。

是否有一些解决方法?

1 个答案:

答案 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的序列化文档。