我有一个大型、复杂的 XML 文档,需要使用 XSLT 进行两次转换才能获得所需的结果。感谢迈克尔在这里的回答,我让它在本地运行转换时完美运行(在终端上使用 xsltproc),但是现在我无法让它以与 PHP 相同的方式工作。
源文档的重要部分如下所示:
<BiographicalNote>
<p>This text includes escaped HTML entities.</p>
</BiographicalNote>
和所需的输出:
<ParagraphStyleRange>
<CharacterStyleRange>
<Content>
This text includes escaped HTML entities.
</Content>
</CharacterStyleRange>
</ParagraphStyleRange>
我需要分两步执行此转换,因为我需要取消转义 HTML 实体,然后将它们处理为 XML 以应用进一步的转换。因此,第一步负责大部分转换,设置最终文档,但是当涉及到带有HTML的部分时,仅执行以下操作:
<xsl:template match="BiographicalNote">
<AuthorBio>
<xsl:value-of select="normalize-space(.)" disable-output-escaping="yes" />
</AuthorBio>
</xsl:template>
然后我有第二个 XSLT 来摆脱<p>
标签,并使用 <em>
、<b>
和 <span>
标签进行各种其他转换:
<xsl:template match="AuthorBio">
<ParagraphStyleRange>
<xsl:apply-templates select="./node()"/>
<Br/>
</ParagraphStyleRange>
</xsl:template>
<xsl:template match="p/text()|text()"> <!-- Not all HTML paragraphs have actual <p> tags. -->
<CharacterStyleRange>
<Content><xsl:value-of select="."/></Content>
</CharacterStyleRange>
</xsl:template>
正如我上面提到的,当我使用 xsltproc 在本地转换它时,这非常有效。但是对于PHP,我得到的是这样的:
<ParagraphStyleRange>
<CharacterStyleRange>
<Content>
<p>This text includes escaped HTML entities.</p>
</Content>
</CharacterStyleRange>
</ParagraphStyleRange>
我已经尝试了PHP代码的一些变体,但这是我最接近的:
function processONIX () {
// Load ONIX file
$onix = new DOMDocument;
$onix->load( 'HarbourONIX20141217.xml' );
// Load ONIXtoICML XSL file
$icml = new DOMDocument;
$icml->load( 'ONIXtoICML.xsl' );
// Configure the transformer
$icmlproc = new XSLTProcessor;
$icmlproc->importStyleSheet($icml);
// Apply ONIXtoICML
return $icmlproc->transformToDoc($onix);
}
function makeICML () {
$temp = processONIX();
// Load Inlines XSL file
$inline = new DOMDocument;
$inline->load( 'Inlines.xsl' );
// Configure the transformer
$inlineproc = new XSLTProcessor;
$inlineproc->importStyleSheet($inline);
// Apply Inlines
$inlineproc->transformToURI($temp, 'ONIX.icml');
}
makeICML();
- 这给出了如上所示的结果,所有 HTML 位都进行了转义。
- 在processONIX()中使用transformToXML不会给我任何东西。
- 在processONIX中使用transformToURI成功保存了文件,并且HTML标记被正确转义:
<AuthorBio><p>This text includes escaped HTML entities.</p></AuthorBio>
但是当我尝试在makeICML()中将其加载为新的DOMDocument时(与processONIX()中的方式相同),它似乎作为空文档加载。不知道这里发生了什么。
问题似乎出在makeICML()函数中,但这似乎很奇怪,因为它与processONIX()几乎完全相同,只是具有不同的变量和文件名。我不知道角色在什么时候被逃脱,所以:我该如何防止它?
disable-output-escaping 属性是序列化程序的指令,除非序列化转换的输出,即将其转换为词法 XML,否则它不起作用。在第一次转换中,您将使用 transformToDoc(),它将输出创建为内存中树,绕过序列化步骤。因此,禁用输出转义不起作用。
在
代码中尚未与问题共享的部分中,您必须有一些错误,例如给定源XML:
$source = <<<XML
<source>
<BiographicalNote>
<p>This text includes escaped HTML entities.</p>
</BiographicalNote>
</source>
XML;
并给出第一个样式表:
$xsltA = <<<XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="BiographicalNote">
<AuthorBio>
<xsl:value-of select="normalize-space(.)" disable-output-escaping="yes" />
</AuthorBio>
</xsl:template>
</xsl:stylesheet>
XSLT;
并给出第二个样式表:
$xsltB = <<<XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="AuthorBio">
<ParagraphStyleRange>
<xsl:apply-templates select="./node()"/>
<Br/>
</ParagraphStyleRange>
</xsl:template>
<xsl:template match="p/text()|text()"> <!-- Not all HTML paragraphs have actual <p> tags. -->
<CharacterStyleRange>
<Content><xsl:value-of select="."/></Content>
</CharacterStyleRange>
</xsl:template>
</xsl:stylesheet>
XSLT;
正在执行应用程序:
echo $source2 = Proc::create($xsltA)->transform($source), "'n----'n";
echo $source3 = Proc::create($xsltB)->transform($source2), "'n";
结果为以下美化输出:
<AuthorBio><p>This text includes escaped HTML entities.</p></AuthorBio>
----
<ParagraphStyleRange>
<CharacterStyleRange>
<Content>This text includes escaped HTML entities.</Content>
</CharacterStyleRange>
<Br/>
</ParagraphStyleRange>
完整示例代码:
<?php
/**
* http://stackoverflow.com/questions/27751339/how-do-i-prevent-character-escaping-in-php-xslt
*/
/**
* Class Proc
*
* XSLT Processor
*/
class Proc
{
private $proc;
/**
* @param string $xslt
*
* @return Proc
*/
public static function create($xslt)
{
return new self($xslt);
}
public function __construct($xslt)
{
$proc = new XSLTProcessor();
$proc->importStylesheet(
$this->docFromString($xslt)
);
$this->proc = $proc;
}
public function transform($source)
{
$result = $this->proc->transformToDoc(
$this->docFromString($source)
);
$result->formatOutput = true;
$result->preserveWhiteSpace = false;
return $result->saveXML($result->documentElement);
}
private function docFromString($string)
{
$doc = new DOMDocument();
$doc->loadXML($string);
return $doc;
}
}
$source = <<<XML
<source>
<BiographicalNote>
<p>This text includes escaped HTML entities.</p>
</BiographicalNote>
</source>
XML;
$xsltA = <<<XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="BiographicalNote">
<AuthorBio>
<xsl:value-of select="normalize-space(.)" disable-output-escaping="yes" />
</AuthorBio>
</xsl:template>
</xsl:stylesheet>
XSLT;
$xsltB = <<<XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="AuthorBio">
<ParagraphStyleRange>
<xsl:apply-templates select="./node()"/>
<Br/>
</ParagraphStyleRange>
</xsl:template>
<xsl:template match="p/text()|text()"> <!-- Not all HTML paragraphs have actual <p> tags. -->
<CharacterStyleRange>
<Content><xsl:value-of select="."/></Content>
</CharacterStyleRange>
</xsl:template>
</xsl:stylesheet>
XSLT;
echo $source2 = Proc::create($xsltA)->transform($source), "'n----'n";
echo $source3 = Proc::create($xsltB)->transform($source2), "'n";