使用XSLT转换XML时保留实体引用?

时间:2011-05-12 23:18:36

标签: xml xslt xslt-2.0

如何在使用XSLT(2.0)转换XML时保留实体引用?使用我尝试过的所有处理器,默认情况下实体都会被解析。我可以使用xsl:character-map来处理字符实体,但是文本实体呢?

例如,这个XML:

<!DOCTYPE doc [
<!ENTITY so "stackoverflow">
<!ENTITY question "How can I preserve the entity reference when transforming with XSLT??">
]>
<doc>
  <text>Hello &so;!</text>
  <text>&question;</text>
</doc>

使用以下XSLT进行转换:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

产生以下输出:

<doc>
   <text>Hello stackoverflow!</text>
   <text>How can I preserve the entity reference when transforming with XSLT??</text>
</doc>

输出应该看起来像输入(减去现在的doctype声明):

<doc>
  <text>Hello &so;!</text>
  <text>&question;</text>
</doc>

希望我不需要用&amp;替换所有&符号(如&amp;question;)来预处理输入,然后对其进行后期处理通过将所有&amp;替换为&来输出。

也许这是特定于处理器的?我正在使用Saxon 9。

谢谢!

5 个答案:

答案 0 :(得分:5)

如果您知道将使用哪些实体以及如何定义它们,您可以执行以下操作(相当原始且容易出错,但仍然比没有更好):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:character-map name="mapEntities">
  <xsl:output-character character="&amp;" string="&amp;"/>
 </xsl:character-map>

 <xsl:variable name="vEntities" select=
 "'stackoverflow',
 'How can I preserve the entity reference when transforming with XSLT\?\?'
 "/>

 <xsl:variable name="vReplacements" select=
 "'&amp;so;', '&amp;question;'"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
  <xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>
]]>
  </xsl:text>

  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:value-of select=
  "my:multiReplace(.,
                   $vEntities,
                   $vReplacements,
                   count($vEntities)
                   )
  " disable-output-escaping="yes"/>
 </xsl:template>

 <xsl:function name="my:multiReplace">
  <xsl:param name="pText" as="xs:string"/>
  <xsl:param name="pEnts" as="xs:string*"/>
  <xsl:param name="pReps" as="xs:string*"/>
  <xsl:param name="pCount" as="xs:integer"/>

  <xsl:sequence select=
  "if($pCount > 0)
     then
      my:multiReplace(replace($pText,
                              $pEnts[1],
                              $pReps[1]
                              ),
                      subsequence($pEnts,2),
                      subsequence($pReps,2),
                      $pCount -1
                      )
      else
       $pText
  "/>
 </xsl:function>
</xsl:stylesheet>

应用于提供的XML文档

<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>
<doc>
    <text>Hello &so;!</text>
    <text>&question;</text>
</doc>

生成了想要的结果

<!DOCTYPE doc [ <!ENTITY so "stackoverflow">
<!ENTITY question
"How can I preserve the entity reference when transforming with XSLT??"> ]>

  <doc>
      <text>Hello &so;!</text>
      <text>&question;</text>
</doc>

请注意

  1. 必须转义替换中的特殊(RegEx)字符。

  2. 我们需要解决DOE,这是不推荐的,因为它违反了XSLT架构和处理模型的原则 - 换句话说,这个解决方案是一个讨厌的黑客。

答案 1 :(得分:3)

如果你使用像S1000D这样的东西,这可能是一个特别棘手的问题。它使用实体和@boardno属性链接到数字。这是其SGML根源的回归。

因为这种自动实体扩展行为是正确但不可取的,所以当使用S1000D作为输入时,我经常不得不回到sed,awk和批处理脚本等工具来管理某些数据分析任务。

恕我直言,对于即将推出的XSLT规范之一,这将是一个很好的变更提议,即兼容处理器接受可以打开和关闭权限扩展的运行时参数。

答案 2 :(得分:1)

如果您使用XSLT 2.0处理器的Java实现(如Saxon 9 Java),您可能想要检查http://andrewjwelch.com/lexev/是否有帮助,您可以使用实体和字符引用预处理XML,以便对它们进行标记作为XML元素,您可以根据需要进行转换。

答案 3 :(得分:1)

我使用此解决方案并且效果很好:

mov x8, xzr

答案 4 :(得分:0)

您可以使用带有&#34;实体&#34;的DOM LS解析器将EntityReference节点保留在文档中。参数设置为true。 http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/DOMConfiguration.html

规范说默认值为true但是根据解析器的不同,它可能是错误的,请注意这一点。

加载Xerces:

DOMImplementationLS domImpl = new org.apache.xerces.dom.CoreDOMImplementationImpl();

您也可以使用下面的注册表,但是人员,我宁愿硬编码我想要的实现,如上所述:

DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS domImpl = (DOMImplementationLS) registry.getDOMImplementation("XML 3.0 LS 3.0"); 

然后,加载您的文档:

// XML parser with XSD schema 
LSParser parser = domImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, "http://www.w3.org/2001/XMLSchema");
DOMConfiguration config = parser.getDomConfig();
config.setParameter("entities", true);
LSInput input = impl.createLSInput();
Document lDoc = parser.parse(your XML stream);

然后,您的XML实体不会在DOM中展开。

然后,因为SAXON不处理未展开的实体(&#39; DOM中不支持的节点类型!5&#39;错误),你不能使用net.sf.saxon.xpath.XPathFactoryImpl,你必须设置Xerces的默认XPathFactory使用XPathFactory.newInstance()