XSLT:如何从特定目录获取文件名?

20
有没有一个函数在XSLT中可以输入目录路径并返回其中的所有文件?
我现在有一个XML文件,读起来是这样的:
<filelist>
    <file>fileA.xml</file>
    <file>fileB.xml</file>
</filelist>

现在有一个名为dir的目录,其中包含文件fileX.xmlfileY.xml和一堆其他的xml文件。我想将这些文件添加到原始xml文件中,以便我可以得到:
<filelist>
    <file>fileA.xml</file>
    <file>fileB.xml</file>
    <file>fileX.xml</file>
    <file>fileY.xml</file>
    .... <!-- other files -->
</filelist>

有没有一种XSLT的方式可以做到这一点?可以接收一个目录根,能够迭代其中的所有文件吗?然后我就可以调用类似这样的东西:

<xsl:element name = file >
     <xsl:copy> <!--whatever file name--> <xsl:copy>
</xsl:element>0

[编辑解决方案]

所有答案都非常有帮助。我最终找到了一个外部解决方案(使用saxon)。我认为将我的解决方案发布在这里可能会对其他人有所帮助,尽管它非常特定于我的情况。

我使用Ant构建Java Web应用程序,并需要在部署之前翻译一些XML文件。因此,我使用 xslt 任务通过将 "saxon9.jar" 添加到类路径中来完成工作。在我的XSL文件中,我只需像这样执行:

<xsl:for-each select="collection('../dir/?select=*.xml')" >
     <xsl:element name='file'>
        <xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/>
     </xsl:element>
</xsl:for-each>

你想让XSLT引擎去文件系统获取目录中的文件列表吗?这不是XSLT的设计初衷。更好的做法是由“宿主”应用程序完成此操作,生成XML,然后使用XLST将其转换为输出格式。 - Guy
3
谢谢您发布解决方案。您应该将其发布为答案,这样我们就可以点赞它。 :-) - LarsH
4
请注意,collection() 不仅适用于 Saxon,它是 XSLT 2.0 的一部分。此外,请注意它仅适用于 XML 文档...不能使用 collection() 获取 .mp3 文件目录等其他文件。 - LarsH
2
好问题。很高兴你坚持使用XSL来完成它,即使其他人建议这不是正确的工具。一旦你弄清楚了,它就变得非常简单。 - Paulb
4个回答

4
XSLT在此任务方面没有内置功能。XSLT是一种转换语言,用于动态输出,通常需要一个包含所有内容的转换源(只是以不同形式存在)-你不能从无中创建XML。
解决这个问题的三种方法是:
1.使用编程语言构建XML,完全不使用XSLT。这是获得所需结果的最简单方法。
2.构建一个接受参数的XSL样式表,在该参数中放置(预先构建的)分隔符文件列表,让XSLT处理字符串并从中生成XML。这也涉及外部处理,基本上是选项1,此外还必须编写能够处理字符串的XSL样式表(这是XSL没有做好的事情)。
3.使用扩展函数并从XSL内部进行目录处理。如何入门的示例可以在我对这个问题的回答中找到。这不是非常可移植的,因为扩展函数是特定于平台的。
归结起来就是:
- 你将需要来自外部编程语言的帮助 - 你不一定需要XSLT来完成任务,因为你只需要XML输出,而不需要进行任何转换。
因此:不要使用XSL来处理此任务。

2
这不再是正确的答案。XPath 2.0 引入了 collection() 函数(请参见编辑后的问题)。 - Christian Hujer
@ChristianHujer 在理想的世界里,您是正确的,但不幸的是,许多XSLT实现到今天仍然只支持XSLT 1.0。 - Tomalak
没错。我知道唯一实现了XSLT 2.0 / XPath 2.0及更高版本的处理器是Saxon,所有其他处理器我所知道的都仅实现了XSLT 1.0 / XPath 1.0,可惜了。 - Christian Hujer
还有一些支持到3.0版本的,所以情况并不像我写这篇答案时那么严峻。但通常你仍然会发现自己被困在1.0版本中。 - Tomalak

1

在本地XSLT中无法实现此操作,但各种实现允许您添加功能扩展。

例如,在C#中,您可以添加用户定义的URN:

 <xsl:stylesheet {snipped the usual xmlns stuff}
    xmlns:user="urn:user" >

然后在 "user" 内使用函数

  <xsl:value-of select="user:getdirectory( @mydir )" />

在C#中,你将"user"与一个C#类关联起来。
 XSLThelper xslthelper      = new XSLThelper( );  // your class here
 xslArgs.AddExtensionObject( "urn:user", xslthelper );

而你的类定义了“getdirectory”函数:

public class XSLThelper
{
public string getdirectory(System.Xml.XPath.XPathNavigator xml, string strXPath, string strNULL)
{
       //blah
    }
}

这里留下了大量编程作业!MSDN 资源

1

Q已经六岁并回答了。对于那些再次到达这里的人,我想提供两分建议。

我需要一个代表目录中文件名的XML文件。我有三种方法:

  1. XSLT 2.0 document(),就像其他人在这个线程中指出的那样。缺点是性能,因为它将文件读入dom解析器,而你真正想要的只是名称。正如LarsH在OP评论中指出的那样,这仅适用于有效的XML文件。如果在递归中有非xml或格式不正确的xml文件,则会导致转换崩溃。

  2. 使用命令xmlstarlet ls > filenames.xml的工具xmlstarlet

  3. 我编写的一个粗糙的bash脚本(可以进行优化):

DIRECTORY=$1
RESULTFILE=$2
# 将目录列表放入临时文件中 # 根据需要修改ls命令 ls -A1 $DIRECTORY > /tmp/zFiles.txt
# 删除垃圾并将行包装在元素中 sed -i -e's/^[^$]*$/ <file filename="&"\/>/' /tmp/zFiles.txt
# 构建文件 echo '<?xml version="1.0" encoding="UTF-8"?>' > $RESULTFILE echo "<files>" >> $RESULTFILE cat /tmp/zFiles.txt >> $RESULTFILE echo "</files>" >> $RESULTFILE

我曾经长时间使用bash脚本,但现在我专门使用xmlstarlet方法。结果的starlet文件包含特定的文件属性,如权限和日期,我发现这很有帮助。


0

您应该能够使用document()函数来读取XML文件。但我不确定它在各种XSLT引擎上的支持程度。

这是一个很好的示例,展示了如何使用它。

但这并没有解决从目录中读取文件名的问题。另一个答案提供了解决方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接