虚拟文件系统是虚拟 Office 文档

作者:Ben Martin

虚拟文件系统可以被制成可写的虚拟 Office 文档。旧的 UNIX 口号“一切皆文件”与 xsltfs:// 虚拟文件系统结合,允许使用 OpenOffice.org 透明地编辑关系数据库、RDF 和任意 XML。

libferris 虚拟文件系统将文件及其元数据都呈现为虚拟文件系统。libferris 认为的文件系统边界包括诸如 PostgreSQL、LDAP 和 Firefox 等有趣的数据源,以及标准的 Web 项目,如 HTTP、FTP 和 RDF。

许多虚拟文件系统允许从其他目录合成目录内容。这方面的经典例子是联合文件系统,它将现有文件系统的集合作为输入,以生成显示基本文件系统集合并集的文件系统。

最近,libferris 文件系统获得了对文件系统执行 XSLT 并将结果公开为虚拟文件系统的支持。为了保持简单,我将原始虚拟文件系统称为输入文件系统,将 XSL 转换产生的文件系统称为转换后的文件系统。由于 XSL 的主要用途是描述树上的转换,这非常适合用于创建转换后的文件系统。

尽管 libferris 文件系统和 XML 数据模型之间存在差异,但也存在许多相似之处。文件的内容映射到 XML 元素的文本内容。文件的元数据由 libferris 作为扩展属性 (EA) 公开,扩展属性映射到文件 XML 元素上的 XML 属性。文件系统和 XML 数据模型之间一个值得注意的区别是,XML 中的文档排序并不总是容易保留。为了保持映射简单,一个文件只能在 XML 文档中生成一个文本节点。从技术上讲,一个 XML 元素可以有多个文本节点作为子节点。

由于与 XML 数据模型关系密切,libferris 文件系统支持将任何文件系统视为文档对象模型 (DOM) 进行查看,该模型是按需创建的。反之亦然:您可以将 DOM 公开为文件系统。由于 libferris 可以将 XML 挂载为文件系统,因此文件系统和 XML 之间的界限在某种程度上变得模糊。

许多现代应用程序将其文档存储为 XML 文件。由于文件系统和 XML 可以通过 libferris 互换,这允许您使用这些应用程序直接编辑文件系统。使用此类应用程序直接编辑文件系统而不是 XML 的主要问题是,应用程序 XML 文件的模式通常与文件系统的布局不匹配。

这就是 xsltfs:// 可以用来创建与应用程序期望的布局相匹配的转换后的文件系统的地方。例如,您可以将 PostgreSQL 数据库中的表作为输入文件系统,并使用 XSL 将该表转换为虚拟电子表格文件,然后将其加载到 OpenOffice.org 中。

当考虑转换后的文件系统中的写入支持时,可能性变得更加有趣。在您对上述 OpenOffice.org 中的虚拟电子表格文件进行了一些更改后,您“保存”该文件。然后,文件系统应用反向 XSLT 并更新输入文件系统(在本例中为 PostgreSQL 表)以反映您的更改。

为了支持这一点,您必须有两个 XSL 文件。第一个样式表将输入文件系统转换为您感兴趣的格式。第二个 XSL 文件(反向样式表)提供反向转换。将来,如果可以从初始转换的操作中推断出第二个 XSL 文件,则它应该是可选的。

反向样式表可以使用每个要更改的文件的显式 URL 或相对路径来指定更新。显式 URL 方法期望反向样式表为每个要更新的文件指定绝对 URL。这对于 xsltfs:// 应用程序可能很方便,在这些应用程序中,URL 在源文件系统和转换后的文件系统中都起作用。例如,当使用 OpenOffice.org 编辑某些 RDF 文件时,主题 URI 将可用,以允许反向样式表使用显式更新。

相对路径方法在概念上类似于将 diff 和 patch 应用于您的文件系统。反向样式表生成要进行的更改列表,对每个要更改的文件使用相对路径。patch 实用程序中的某些选项也可用于反向样式表。根元素可以包含一个 strip 属性,其工作方式类似于 patch 的 strip 选项。autocreate 属性设置为 true 时,将使 libferris 尝试创建新文件,其中反向样式表指定了源文件系统中不存在的相对路径。

目前,两个反向样式表都必须提供每个要更新的文件的完整内容。这不是一个主要的缺点,因为该信息已经完全可在转换后的文件系统中获得。

以下部分显示了两个用途:创建新的虚拟文件系统并直接从控制台与之交互,以及创建虚拟 Office 文档。接下来是一些关于手动创建自定义样式表的建议。

使用 xsltfs:// 创建文件系统

可以通过 xsltfs:// 方案访问转换后的文件系统。可以使用 libferris 客户端与此文件系统交互,或者通过 Linux 内核使用用户空间文件系统 (FUSE) 公开它。

由于 libferris 允许您将 XML 文件视为文件系统,因此清单 1 中显示的 XML 文件将用作输入文件系统。

清单 1. example.xml

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>
  <file1 size="200"/>
  <file3>filesystems inside XML?</file3>
  <file7 myattr="foo" >Something blue</file7>
</root>

清单 2 中显示的 XSL 文件将从输入文件系统创建我们的转换后的文件系统。重要的是要记住,尽管在本例中输入文件系统是从 XML 文件生成的,但它同样可以来自挂载的 LDAP 服务器的数据。XSL 将在文档根元素下创建两个元素。file3 元素将包含输入文件系统中 file3 虚拟“文件”的原始内容。file7 将把属性 myattr 作为其内容。

清单 2. example.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  
  version="1.0"
  >
  <xsl:output method="xml"/>

  <xsl:template match="/">
    <root>
      <xsl:apply-templates/>
      </root>
  </xsl:template>

  <xsl:template match="file3">
    <context original-url="{@url}" name="file3">
        <xsl:value-of select="@content"/>
    </context>
  </xsl:template>

  <xsl:template match="file7">
    <context original-url="{@url}" name="file7">
        <xsl:value-of select="@myattr"/>
    </context>
  </xsl:template>
</xsl:stylesheet>

转换后的文件系统可以像任何其他文件系统一样使用命令行实用程序 ferrisls、fcat、ferriscp 等。libferris 中的 xsltfs:// URL 方案位于大多数其他 URL 方案之上,并允许您通过提供要应用的 XSL 转换在任何点物化文件系统。XSL 文件本身的位置是根据您在 libferris 中设置的 xsltfs 路径确定的。使用 xsltfs 路径避免了将完整的样式表路径嵌入到 xsltfs:// URL 中。由于样式表是使用类似 CGI 的语法指定的,因此避免使用 / 字符意味着 xsltfs:// 中的文件名没有歧义。

您可以在虚拟文件系统中的任何点应用样式表。将样式表应用于 example.xml 文件系统的结果将成为根目录为 example.xml?stylesheet=example.xsl 虚拟目录的目录的内容。

在 xsltfs:// 参数中不使用任何 / 的情况下,文件名和参数一起用于指定 xsltfs:// 按需创建的虚拟目录的名称。由于没有歧义,您可以直接导航到以该虚拟目录为根目录的转换后的文件系统中。这在下面的示例中显示。

清单 3 中显示了文件系统的一部分,以使事情更清楚。我已经使用特殊的类似 CGI 的语法将 foo.xsl 应用于 example.xml 文件,以命名虚拟目录。libferris 为我创建了这个虚拟目录,以允许直接导航到转换后的文件系统中。rootElement 是转换后的文件系统的根;用 XML 术语来说,它是将 foo.xsl 样式表应用于以 example.xml 为根目录的文件系统的结果的文档根。文件系统位于 xsltfs:// 的上下文子目录中,以便稍后在 xsltfs:// 中完成其他参数和扩展。

清单 3. 为 example.xml 生成转换后的文件系统

xsltfs://
  context
    file
      tmp
        example.xml
        example.xml?stylesheet=foo.xsl
          rootElement
            myFoo1
            myBar2

可以使用 ferris-capplet-general 配置工具的 XSLT 样式表页面设置 xsltfs 路径。除了使用 ferris-capplet-general 设置 XSLT 路径外,您还可以使用 LIBFERRIS_XSLTFS_SHEETS_URL 环境变量显式传入前向和反向样式表所在的路径。这使得在 shell 脚本中使用带有 FUSE 模块的 xsltfs 非常简单,因为您不需要安装样式表文件。样式表可以存储在 libferris 可以访问的任何文件系统中。

为了本示例的目的,我将文件和样式表存储在 file://tmp/example 中。如果我正在从 example 目录运行我的示例,则将 . 放入我的 XSLT 路径就足够了——请参阅清单 4 中的示例。

清单 4. 探索我们的新 XSLT 文件系统

$ bash
$ URL='xsltfs://context/file/tmp/example/
↪example.xml?stylesheet=example.xsl'
$ cd /tmp/example
$ ls
example-rev.xsl  example.xml  example.xsl
$ export LIBFERRIS_XSLTFS_SHEETS_URL=`pwd`
$ ferrisls -l $URL
   0 root
$ ferrisls -l $URL/root
  23 file3
   3 file7

$ fcat $URL
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>

 <context name="file3"
 original-url="file:///tmp/example/example.xml/root/file3"
  >filesystems inside XML?</context>

  <context name="file7"
    original-url="file:///tmp/example/example.xml/root/file7"
  >foo</context>

</root>

$ fcat $URL/root/file3
filesystems inside XML?

当我们提供反向样式表时,事情变得更有趣,如清单 5 所示。在这种情况下,我们将事物相当简单地映射回它们在输入文件系统中的原始位置。file7 内容被放回输入文档的 myattr XML 属性中。拥有显式的反向 XSL 转换使您可以自由地仅更新原始文件系统的一部分,如您认为合适的那样。您还可以使用样式表中的函数来修改返回到输入文件系统的数据。

清单 5. example-rev.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  
  version="1.0"
  
  exclude-result-prefixes="ferris"
  >
  <xsl:output method="xml"/>

  <xsl:template match="/">
    <explicit-updates>
        <xsl:apply-templates/>
      </explicit-updates>
  </xsl:template>

  <xsl:template match="context[@name='file3']">
    <context url="{@original-url}">
      <xsl:value-of select="."/>
    </context>
  </xsl:template>

  <xsl:template match="context[@name='file7']">
    <attribute url="{@original-url}"
name="myattr"><xsl:value-of select="."/></attribute>
  </xsl:template>

</xsl:stylesheet>

现在我们有了前向和反向 XSL,我们可以通过与我们的 xslfs:// 中的虚拟文件交互来愉快地修改原始 example.xml 文件的内容,如清单 6 所示。

清单 6. 通过我们的新 XSLT 文件系统更改 XML 文件

$ bash
$ URL='xsltfs://context/file/tmp/example/
↪example.xml?stylesheet=example.xsl&
↪reverse-stylesheet=example-rev.xsl'
# Change the file3 element to have new content
$ echo foo | ferris-redirect -T $URL/root/file3
$ cat example.xml
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>
  <file1 size="200"/>
  <file3>foo
</file3>
  <file7 myattr="foo">Something blue</file7>
</root>

# Update everything based on a new XML file
$ cat example-update1.xml
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>
   <context name="file3" original-url=
↪"file:///tmp/example/example.xml/root/file3"
>A new file3 text node
   </context>
   <context name="file7" original-url=
↪"file:///tmp/example/example.xml/root/file7"
>A new file7 myattr</context>
</root>

$ cat example-update1.xml | ferris-redirect -T $URL

$ cat example.xml
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>
  <file1 size="200"/>
  <file3>A new file3 text node
   </file3>
  <file7 myattr="A new file7 myattr"
>Something blue</file7>
</root>

清单 6 中的示例显示了更新文件系统的两个选项:通过更改单个虚拟文件或通过一次性更新虚拟 XML 文档(转换后的文件系统)。第一种更新单个文件的方法在 xsltfs 中维护了文件系统隐喻。第二种通过重写主虚拟 XML 文档进行更新的方法为 XML 编辑应用程序(如 OpenOffice.org)提供支持,其中文档被读取、操作和重写。

URL 可能非常丑陋且相当长。如果您经常编辑此类文件系统,您可能希望使用 FUSE 公开 xsltfs。使用 OpenOffice.org 编辑虚拟 XML 文件需要使用 FUSE 通过 Linux 内核公开虚拟 XML 文件。

虚拟 Office 文档

如果 xsltfs:// 的输出格式是众所周知的,例如 OpenOffice.org 文档,您可以从 XSL 文件自动创建文件格式。

ferris-filesystem-to-xsltfs-sheets 客户端用于自动设置样式表。插件系统用于允许将来支持新的文件格式。要查看哪些插件可用,请使用--plugin=help命令行选项。

您需要使用 FUSE 文件系统才能直接读取和写入虚拟 Office 文档。这也可以通过 ferris-filesystem-to-xsltfs-sheets 客户端使用--fuse=foo命令行选项。

自动设置。某些发行版需要为用户进行额外的设置才能使用 FUSE 挂载。在 Fedora Core 上,您必须将用户添加到 fuse 组,这可以如清单 7 所示完成。

清单 7. 允许用户在 Fedora Core 上使用 FUSE

root-bash-# usermod -a -G fuse ben

清单 8 显示了一个设置小型 PostgreSQL 表并创建一个新的虚拟 Office 文档以允许编辑此表的示例。

清单 8. 设置虚拟 Office 文档以编辑数据库表

bash-$ psql
ben=# create database lj;
ben=# \c lj;
You are now connected to database "lj".
lj=# create table msgs
lj-# ( id serial primary key,
lj-#   num int, msg varchar(200),
lj-#   foo varchar(100) );
lj=# insert into msgs values
lj-# ( default, 7,  'This is msg #1', 'Foo is Bar');
lj=# insert into msgs values
lj-# ( default, 12, 'Second message', 'ii tenki');
lj=# select * from msgs;
 id | num |      msg       |    foo
----+-----+----------------+------------
  1 |   7 | This is msg #1 | Foo is Bar
  2 |  12 | Second message | ii tenki
(2 rows)
\q

bash-$ ferrisls pg:///lj
msgs
bash-$ ferrisls --xml pg:///lj/msgs
<ferrisls>
<ferrisls url="pg:////lj/msgs" name="msgs">
 <context  id="1"  num="7"
    msg="This is msg #1"  foo="Foo is Bar"
    name="1"  primary-key="id"  />
 <context  id="2"  num="12"
    msg="Second message"  foo="ii tenki"
    name="2"  primary-key="id"  />
</ferrisls>
</ferrisls>

bash-$ ferris-filesystem-to-xsltfs-sheets \
  --plugin excel2003 --fuse msgs \
  pg:///lj/msgs

bash-$ ferrisls -lh ~/ferrisfuse
... ben ben 129  06 Oct 21 11:56 mount-msgs.sh
... ben ben 4.0k 06 Oct 21 11:56 msgs

bash-$ cd ~/ferrisfuse/
bash-$ ./mount-msgs.sh
bash-$ ls -lh msgs
... 0 ben ben 3.8K Jan  1  1970 msgs.xml*
bash-$ cat msgs/msgs.xml | head
<?xml version="1.0" encoding="UTF-8" ... ?>
<Workbook xmlns=...>
 <OfficeDocumentSettings xmlns=...>
    <Colors>
  ...

bash-$ ooffice msgs/msgs.xml

清单 8 中的最后一个命令打开虚拟电子表格文档,它应该看起来类似于图 1。然后我更改了第二行中的一些数据并保存了文件,给出了图 2 中显示的结果。

Virtual Filesystems Are Virtual Office Documents

图 1. 虚拟 Office 文档的初始视图

Virtual Filesystems Are Virtual Office Documents

图 2. 对第二行的一些更改已保存回数据库。

在保存虚拟 Office 文档后查看 PostgreSQL 表会显示更新后的内容——请参阅清单 9。

清单 9. 使用 OpenOffice.org 编辑后的数据库内容

bash-$ psql lj
lj=# select * from msgs;
 id | num |      msg       |               foo
----+-----+----------------+-----------------------
  1 |   7 | This is msg #1 | Foo is Bar
  2 |  23 | Second message | The weather outside...
(2 rows)
Google Earth 和 xsltfs://

ferris-mount-etagere-as-kml.sh 脚本使用 xsltfs:// 和 FUSE 来设置读/写虚拟 KML 文件。样式表在 libferris geoemblems 和 Google Earth 使用的地名 KML 格式之间进行转换。

用于公开 libferris emblems 的样式表提供了一个将 libferris 中的整个树转换为分层 XML 文档以供外部应用程序使用的示例。来自输入文件系统的 is-dir EA 用于确定要在转换后的文件系统中生成的 XML 元素的类型,因为 KML 文件需要使用 Placemark 或 Folder 元素,具体取决于是否要查找子元素。

滚动自定义样式表

出于测试目的,如果设置了 LIBFERRIS_XSLTFS_DONT_UPDATE 环境变量,libferris 将执行反向样式表应用程序并记录将要完成的更新,但实际上不会更新输入文件系统。

有一些提示可以使设置和调整自定义前向和反向样式表变得更加简单。

我再次在此处使用清单 1 中显示的 example.xml 文件作为输入文件系统。虽然在本例中,我从 example.xml(一个 XML 文件)开始,但我们想看看 libferris 如何看待这个输入文件系统,而不仅仅是原始 XML 本身。例如,当 libferris 挂载此 XML 文件时,元素的文本节点的内容将作为 content 属性可用。

为了获得 libferris 对 XML 的视图,我将 ferrisls 与其 --xml-xsltfs-debug 选项一起使用。我还​​需要递归 example.xml 文件以获取整个文件系统,并显式选择 example.xsl 文件将要使用的任何属性。

清单 10 中显示了手动应用前向样式表。

反向样式表可以应用于转换后的文件系统 XML 文件。一旦此输出看起来正常,就可以通过设置 LIBFERRIS_XSLTFS_DONT_UPDATE 的 xsltfs:// 应用它来完成非破坏性测试。确保在 ferris-capplet-logging 配置工具中将 ferris-logging-xsltfs 设置为 debug,以获取有关将要更新的所有信息。

清单 10. 开发和调试新样式表

$ ferrisls -R --xml-xsltfs-debug \
  --show-ea=name,content,myattr \
  example.xml/root

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<ferrisls>

  <root name="root"
     url="file:///tmp/KK/example.xml/root">

    <file1 content="" myattr=" " name="file1"
       url="file:///tmp/KK/example.xml/root/file1"/>
    <file3 content="filesystems inside XML?"
       myattr=" " name="file3"
       url="file:///tmp/KK/example.xml/root/file3"/>
    <file7 content="Something blue" myattr="foo"
       name="file7"
       url="file:///tmp/KK/example.xml/root/file7"/>
  </root>

</ferrisls>

$ ferrisls -R --xml-xsltfs-debug \
  --show-ea=name,content,myattr \
  example.xml/root >| input.xml

$ FerrisXalanTransform  -s example.xsl  -m input.xml
transform XML:input.xml with xsl:example.xsl
<?xml version="1.0" encoding="UTF-8"?><root>
 <context
original-url="file:///tmp/KK/example.xml/root/file3"
   name="file3">filesystems inside XML?
 </context>
 <context
original-url="file:///tmp/KK/example.xml/root/file7"
   name="file7">foo
 </context>
</root>

$ export LIBFERRIS_XSLTFS_SHEETS_URL=`pwd`
$ URL=xsltfs://context/file/tmp/example/example.xml/
↪root?stylesheet=example.xsl
$ fcat $URL
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<root>
 <context name="file3" original-url="file:///home/ben/xsltfs/
↪example.xml/root/file3">filesystems inside XML?
 </context>
 <context name="file7" original-url="file:///home/ben/
↪xsltfs/example.xml/root/file7">foo
 </context>
</root>
$ fcat $URL >| translated.xml
$ vi translated.xml
...make changes to test reverse sheet
...inserting CHANGE_A and changeB into the elements

$ FerrisXalanTransform -s example-rev.xsl \
   -m translated.xml

transform XML:translated.xml with xsl:example-rev.xsl
<?xml version="1.0" encoding="UTF-8"?>
<explicit-updates>
 <context
url="file:///home/ben/xsltfs/example.xml/root/file3"
  >filesystems inside CHANGE_A XML?
 </context>
 <attribute
url="file:///home/ben/xsltfs/example.xml/root/file7"
   name="myattr">foo changeB
 </attribute>
</explicit-updates>
一些未来方向

主要的计划功能是自动推导反向样式表。这将使设置 xsltfs:// 挂载点变得更加简单。诸如复制前向 XSL 文件中的节点之类的操作将需要显式的反向 XSL 文件来解决冲突,其中每个副本都在转换后的文件系统中被编辑。

ferris-filesystem-to-xsltfs-sheets 的更多插件正在计划中。例如,能够编辑来自常见 LDAP 模式的数据(如 OpenOffice.org 中的用户身份验证)将会很好。支持创建虚拟 OpenOffice.org zip 文件作为 xsltfs:// 的目标将允许创建原生 OpenOffice.org 文档。

patch 的更多命令行选项可能会用于反向样式表。

本文资源: /article/9513

Ben Martin 从事文件系统工作已超过十年。他目前正在澳大利亚伍伦贡大学攻读博士学位,将语义文件系统与形式概念分析相结合,以改善人与文件系统的交互。

加载 Disqus 评论