如何防止 PHP XSLT 中的字符转义


How do I prevent character escaping in PHP XSLT?

我有一个大型、复杂的 XML 文档,需要使用 XSLT 进行两次转换才能获得所需的结果。感谢迈克尔在这里的回答,我让它在本地运行转换时完美运行(在终端上使用 xsltproc),但是现在我无法让它以与 PHP 相同的方式工作。

源文档的重要部分如下所示:

<BiographicalNote>
 &lt;p&gt;This text includes escaped HTML entities.&lt;/p&gt;
</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>
   &lt;p&gt;This text includes escaped HTML entities.&lt;/p&gt;
  </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>
 &lt;p&gt;This text includes escaped HTML entities.&lt;/p&gt;
</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>
 &lt;p&gt;This text includes escaped HTML entities.&lt;/p&gt;
</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";