重写多个文件并避免错误 XTRE1500:无法读取在同一转换过程中写入的文档
Rewrite multiple files and avoid error XTRE1500: Cannot read a document that was written during the same transformation
在尝试解决 问题时,我切换到 Saxon XSLT 处理器并且一直在努力让我的代码语法正常工作。该代码的目的是遍历 HTML 文件列表,在每个页面中找到任何 header 的第一个实例并将其转换为 H1(因为我们必须将 H2s 用于 PDF 输出但我们的 HTML 输出需要 H1。
我从一个批处理文件开始:
set outputDir=%1
@set Saxon=C:\Users\%username%\saxon\saxon9he.jar
REM Create filelist
dir %outputDir%\*.htm /b /s /A-D > file_list.txt
@echo ^<filelist^>^</filelist^> > pre_filelist.xml
REM XML-ize filelist
java -cp %Saxon% net.sf.saxon.Transform -s:pre_filelist.xml -xsl:convert_filelist.xsl -o:pre_list.xml
REM Replace starting h2 tags with h1 tags
java -cp %Saxon% net.sf.saxon.Transform -s:pre_list.xml -xsl:h2toh1.xsl -o:null.xml
REM Garbage collection
DEL pre_list.xml
DEL pre_filelist.xml
DEL file_list.txt
pause
找到所有输出 HTML 文件并使用 convert_filelist.xsl:
将它们格式化为列表
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Set output style. XML with no indentations -->
<xsl:output indent="no" method="xml" omit-xml-declaration="yes"/>
<!-- Reads the file list text file into memory as a global variable. -->
<xsl:variable name="fileList">file_list.txt</xsl:variable>
<!-- Parses the file list text file to create an XML list of files that can be fed to the transformer -->
<xsl:template match="filelist">
<!-- Create a variable that can be parsed -->
<xsl:variable name="filelist_raw"><xsl:value-of select="unparsed-text($fileList,'UTF-8')"/></xsl:variable>
<!-- Create a open and close file tags for each line in the list -->
<xsl:variable name="driveLetter"><xsl:value-of select="substring-before(unparsed-text($fileList,'UTF-8'),':')"/>:<xsl:text disable-output-escaping="yes">\</xsl:text></xsl:variable>
<xsl:variable name="driveLetterReplacement"><xsl:text disable-output-escaping="yes"><file>file:///</xsl:text><xsl:value-of select="$driveLetter"/></xsl:variable>
<!-- Generate an xml tree. The value-of is doing a text-level replacement. Looking for the drive letter and replacing it -->
<!-- with the file open tag and drive letter. Looking for the file extension and replacing with the extension and file close tag. -->
<file_list><xsl:value-of select="replace(replace(replace($filelist_raw,'.htm','.htm</file>'),$driveLetter,$driveLetterReplacement),'\','/')" disable-output-escaping="yes"/></file_list>
</xsl:template>
</xsl:stylesheet>
然后使用 h2toh1.xsl:
将第一个 header 转换为 H1
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:MadCap="http://www.madcapsoftware.com/Schemas/MadCap.xsd"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Set output style. XML with no indentations. Normally no. -->
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<!-- Begin traversing the list of files in the output folder. -->
<xsl:template match="file_list">
<xsl:for-each select="*">
<xsl:variable name="filename" select="."/>
<xsl:variable name="content" select="document($filename)"/>
<!-- Generate a new output file to replace the Flare generated file. Uses the same file name. Transparent to the end user. -->
<xsl:result-document href="{$filename}" method="xml">
<xsl:apply-templates select="document($filename)">
<xsl:with-param name="content" select="$content"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<!-- Recreate each node as it appears in the generated document -->
<xsl:template match="*">
<xsl:param name="content"/>
<xsl:variable name="name" select="name(.)"/>
<xsl:element name="{$name}">
<xsl:for-each select="@*">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- Select the first header and change it to an h1. -->
<xsl:template match="*[matches(name(), 'h\d')][1]">
<xsl:element name="h1">
<xsl:for-each select="@*|node()">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
但是,我随后收到列表中每个文件的这些错误实例:
Warning at char 9 in xsl:variable/@select on line 13 column 63 of h2toh1.xsl: XTRE1500: Cannot read a document that was written during the same transformation: file:///C:/TechDocs/Projects/ScriptTest/Output/JPittman/Docs11/Default.htm
Warning at char 9 in xsl:apply-templates/@select on line 17 column 55 of h2toh1.xsl: XTRE1500: Cannot read a document that was written during the same transformation: file:///C:/TechDocs/Projects/ScriptTest/Output/JPittman/Docs11/Default.htm
我了解问题的原因,但不知道如何解决。我还尝试使用 collection 函数,因为无论如何重写每一页似乎都很笨拙,但我不明白如何实现它。有帮助吗?
规范中定义错误的原因是未定义执行顺序,因此如果您在转换中读取和写入同一文件,那么原则上无法预测读取是否在之前完成写,或之后。 (当然,在实践中,往往不是这样,因为会有函数依赖。)
通常情况下,您可以通过对读取和写入使用略有不同的 URL 来绕过该限制,但风险自负。例如,URI 末尾的查询参数(如 ?version=1)通常会在 file:///
个 URI 上被忽略。
在尝试解决
我从一个批处理文件开始:
set outputDir=%1
@set Saxon=C:\Users\%username%\saxon\saxon9he.jar
REM Create filelist
dir %outputDir%\*.htm /b /s /A-D > file_list.txt
@echo ^<filelist^>^</filelist^> > pre_filelist.xml
REM XML-ize filelist
java -cp %Saxon% net.sf.saxon.Transform -s:pre_filelist.xml -xsl:convert_filelist.xsl -o:pre_list.xml
REM Replace starting h2 tags with h1 tags
java -cp %Saxon% net.sf.saxon.Transform -s:pre_list.xml -xsl:h2toh1.xsl -o:null.xml
REM Garbage collection
DEL pre_list.xml
DEL pre_filelist.xml
DEL file_list.txt
pause
找到所有输出 HTML 文件并使用 convert_filelist.xsl:
将它们格式化为列表<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Set output style. XML with no indentations -->
<xsl:output indent="no" method="xml" omit-xml-declaration="yes"/>
<!-- Reads the file list text file into memory as a global variable. -->
<xsl:variable name="fileList">file_list.txt</xsl:variable>
<!-- Parses the file list text file to create an XML list of files that can be fed to the transformer -->
<xsl:template match="filelist">
<!-- Create a variable that can be parsed -->
<xsl:variable name="filelist_raw"><xsl:value-of select="unparsed-text($fileList,'UTF-8')"/></xsl:variable>
<!-- Create a open and close file tags for each line in the list -->
<xsl:variable name="driveLetter"><xsl:value-of select="substring-before(unparsed-text($fileList,'UTF-8'),':')"/>:<xsl:text disable-output-escaping="yes">\</xsl:text></xsl:variable>
<xsl:variable name="driveLetterReplacement"><xsl:text disable-output-escaping="yes"><file>file:///</xsl:text><xsl:value-of select="$driveLetter"/></xsl:variable>
<!-- Generate an xml tree. The value-of is doing a text-level replacement. Looking for the drive letter and replacing it -->
<!-- with the file open tag and drive letter. Looking for the file extension and replacing with the extension and file close tag. -->
<file_list><xsl:value-of select="replace(replace(replace($filelist_raw,'.htm','.htm</file>'),$driveLetter,$driveLetterReplacement),'\','/')" disable-output-escaping="yes"/></file_list>
</xsl:template>
</xsl:stylesheet>
然后使用 h2toh1.xsl:
将第一个 header 转换为 H1<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:MadCap="http://www.madcapsoftware.com/Schemas/MadCap.xsd"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Set output style. XML with no indentations. Normally no. -->
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<!-- Begin traversing the list of files in the output folder. -->
<xsl:template match="file_list">
<xsl:for-each select="*">
<xsl:variable name="filename" select="."/>
<xsl:variable name="content" select="document($filename)"/>
<!-- Generate a new output file to replace the Flare generated file. Uses the same file name. Transparent to the end user. -->
<xsl:result-document href="{$filename}" method="xml">
<xsl:apply-templates select="document($filename)">
<xsl:with-param name="content" select="$content"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
<!-- Recreate each node as it appears in the generated document -->
<xsl:template match="*">
<xsl:param name="content"/>
<xsl:variable name="name" select="name(.)"/>
<xsl:element name="{$name}">
<xsl:for-each select="@*">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- Select the first header and change it to an h1. -->
<xsl:template match="*[matches(name(), 'h\d')][1]">
<xsl:element name="h1">
<xsl:for-each select="@*|node()">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
但是,我随后收到列表中每个文件的这些错误实例:
Warning at char 9 in xsl:variable/@select on line 13 column 63 of h2toh1.xsl: XTRE1500: Cannot read a document that was written during the same transformation: file:///C:/TechDocs/Projects/ScriptTest/Output/JPittman/Docs11/Default.htm
Warning at char 9 in xsl:apply-templates/@select on line 17 column 55 of h2toh1.xsl: XTRE1500: Cannot read a document that was written during the same transformation: file:///C:/TechDocs/Projects/ScriptTest/Output/JPittman/Docs11/Default.htm
我了解问题的原因,但不知道如何解决。我还尝试使用 collection 函数,因为无论如何重写每一页似乎都很笨拙,但我不明白如何实现它。有帮助吗?
规范中定义错误的原因是未定义执行顺序,因此如果您在转换中读取和写入同一文件,那么原则上无法预测读取是否在之前完成写,或之后。 (当然,在实践中,往往不是这样,因为会有函数依赖。)
通常情况下,您可以通过对读取和写入使用略有不同的 URL 来绕过该限制,但风险自负。例如,URI 末尾的查询参数(如 ?version=1)通常会在 file:///
个 URI 上被忽略。