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

时间:2015-01-03 03:06:32

标签: php xml xslt escaping

我有一个庞大而复杂的XML文档,需要使用XSLT进行两次转换才能获得所需的结果。感谢michael's answer here,我在本地运行转换(在终端上使用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()几乎完全相同,只有不同的变量和文件名。我无法弄清楚角色何时被逃脱,所以:我该如何防止呢?

2 个答案:

答案 0 :(得分:1)

disable-output-escaping属性是序列化程序的指令,除非您序列化转换的输出,即将其转换为词法XML,否则它不起作用。在第一次转换中,您使用的是transformToDoc(),它将输出创建为内存中的树,绕过序列化步骤。所以disable-output-escaping没有效果。

答案 1 :(得分:0)

在代码中未与您的问题共享的部分中必须有一些错误,如同给定源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";