如何使用已删除的源元素打印XSLT结果文档?
I have a source XHTML document with elements in multiple namespaces that I am transforming into an HTML document (obviously with no namespaces). In my XSL templates I only match elements in the XHTML namespace to remove non-HTML-compatible elements from the result tree. However, in the output, while those elements are gone, the whitespace I used to indent them remains—i.e., lines of irrelevant CR/LFs and tabs.
For example, if this is my input:
<div id="container">
<svg:svg>
<svg:foreignObject>
<img />
</svg:foreignObject>
</svg:svg>
</div>
After applying the transformation, this will be the output:
<div id="container">
<img />
</div>
While my desired output is this:
<div id="container">
<img />
</div>
This happens using both TransforMiiX (attaching the stylesheet locally in Firefox) and libxslt (attaching the stylesheet server-side with PHP), so I know it's probably the result of some XSL parameter not getting set, but I've tried playing with <xsl:output indent="yes|no" />
, xml:space="default|preserve"
, <xsl:strip-space elements="foo bar|*" />
, all to no avail.
This will be implemented server-side so if there's no way to do it in raw XSL but there is a way to do it in PHP I'll accept that.
I know this is not a namespace issue since I get the same result if I remove ANY element.
我有一个源XHTML文档,其中包含多个名称空间中的元素,我正在将其转换为HTML文档(显然没有名称空间) )。 在我的XSL模板中,我只匹配XHTML命名空间中的元素,以从结果树中删除非HTML兼容的元素。 但是,在输出中,当这些元素消失时,我用来缩进它们的空格仍然是 - 即不相关的CR / LF和标签行。 p>
例如,如果这是我的 输入: p>
&lt; div id =“container”&gt;
&lt; svg:svg&gt;
&lt; svg:foreignObject&gt;
&lt; img /&gt;
&lt; / svg:foreignObject&gt;
&lt; / svg:svg&gt;
&lt; / div&gt;
code> pre>
应用转换后,这将是 输出: p>
&lt; div id =“container”&gt;
&lt; img /&gt;
&lt; / div&gt;
code> pre>
我的所需 em>输出是这样的: p>
&lt; div id =“container “&gt;
&lt; img /&gt;
&lt; / div&gt;
code> pre>
使用TransforMiiX(在Firefox中本地附加样式表)和libxslt( 使用PHP附加样式表服务器端),所以我知道这可能是某些XSL参数未设置的结果,但我尝试使用&lt; xsl:output indent = “是|否”/&gt; code>, xml:space =“default | preserve” code>,&lt; xsl:strip-space elements =“foo bar | *”/&gt ; code>,一切都无济于事。 p>
这将在服务器端实现,所以如果在原始XSL中无法做到这一点,但有一种方法可以在PHP中实现 我会接受的。 p>
我知道这不是名称空间问题,因为如果删除任何元素,我会得到相同的结果。 p>
div>
The white space you see is from the source document. XSLT default rules say that text nodes should be copied, it does not matter if they are empty or not. To override the default rule, include:
<xsl:template match="text()" />
Alternatively: Spot any <xsl:apply-templates />
(or <xsl:apply-templates select="node()" />
) and explicitly specify which children you want to apply templates to. This method might be necessary if your transformation partly relies on the identity template (in which case the empty template for text nodes would be counter-productive).
I have marked up the "insignificant" white space in your snippet the way Word would do it:
<div id="container">¶
····<svg:svg>¶
········<svg:foreignObject>¶
············<img />¶
········</svg:foreignObject>¶
····</svg:svg>¶
</div>
EDIT: You can also modify your identity template like this:
<xsl:template match="node() | @*">
<xsl:copy>
<!-- select everything except blank text nodes -->
<xsl:apply-templates select="
node()[not(self::text())] | text()[normalize-space() != ''] | @*
" />
</xsl:copy>
</xsl:template>
This would remove any blank-only text node (attribute values remain untouched, they are not text nodes). Use <xsl:output indent="yes" />
to pretty-print the result.
You have two ways to achieve your desired result: either you fix your original transformation to handle whitespace differently, or you keep your transformation as-is and you add a second pass to prettify the output. If your original transformation is complicated then I'd recommend the 2-pass approach. You don't want to make your transformation even more complicated or you'll create some corner cases where you don't get the desired results and you'll have to add more special case handling and potentially add bugs to something that used to work, etc...
You should be able to ignore the whitespace nodes by testing them with normalize-text()
. Here's how the second pass could look like. If you go with the 1-pass approach, the code will be roughly the same I guess.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="text()">
<xsl:if test="normalize-space(.) != ''">
<xsl:value-of select="."/>
</xsl:if>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>