2011-05-12 55 views
7

如何在使用XSLT(2.0)轉換XML時保留實體引用?在我嘗試過的所有處理器中,默認情況下都會解析實體。我可以使用xsl:character-map來處理字符實體,但是文本實體呢?在使用XSLT轉換XML時保留實體引用?

例如,該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> 

輸出應看起來像輸入(減去現在的文檔類型聲明):

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

希望,我沒有更換所有&符號與&替換所有&amp;預先處理的輸入與&amp;(如&amp;question;),然後處理後的輸出。

也許這是處理器特定的?我正在使用撒克遜9.

謝謝!

+0

好問題,+1。所請求的處理幾乎不可能用XSLT完成,我不會經常推薦使用我的答案。 – 2011-05-13 03:12:37

回答

4

如果你知道的實體將被用於什麼,他們是如何定義的,你可以做以下(相當原始,而且容易出錯,但仍比沒有好):

<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. 在替代的特殊(正則表達式)字符必須轉義。

  2. 我們需要解決DOE,這是不推薦的,因爲它違反了XSLT架構和處理模型的原則 - 換句話說,這個解決方案是一個討厭的黑客。

+0

非常感謝Dimitre。我很害怕這個。不幸的是,我不知道正在使用什麼實體。我想我會堅持這個項目的OmniMark。儘管你的回答非常有幫助,但我很欣賞時間。 +1並接受回答 – 2011-05-13 03:50:36

+0

@DevNull:不客氣。 – 2011-05-13 04:02:10

1

如果您使用的是Java實現的XSLT 2.0處理器(如撒克遜9的Java),你可能要檢查http://andrewjwelch.com/lexev/是否幫助了,你可以用,讓他們打上這樣的實體和字符引用進行預處理的XML作爲XML元素,您可以根據需要進行轉換。

+0

謝謝馬丁。我會檢查一下。 +1 – 2011-05-13 15:47:51

3

如果您使用類似S1000D的東西,這可能是一個特別麻煩的問題。它使用實體和@boardno屬性鏈接到數字。這是它的SGML根源的倒退。

由於這種自動實體擴展行爲正確但不可取,因此在使用S1000D作爲輸入時,我經常需要退回到像sed,awk和批處理腳本這樣的工具來管理某些數據分析任務。對於即將到來的XSLT規範之一,這將是一個很好的變更建議,即兼容的處理器接受一個可以打開和關閉entitiy擴展的運行時參數。

+0

我主要處理ATA iSpec 2200,並與S1000D一起工作,所以我確切地知道你的意思。 – 2012-11-29 01:56:31

0

通過使用「實體」參數設置爲true的DOM LS分析器,可以將EntityReference節點保留在文檔中。 http://docs.oracle.com/javase/6/docs/api/org/w3c/dom/DOMConfiguration.html

該規範說默認值是true,但取決於解析器,它可能是錯誤的,請注意這一點。

要加載的Xerces:

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

您可以使用註冊表如下太多,但personnaly,我寧願寫死我如上要實現:

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中展開。 ,你不能使用net.sf.saxon.xpath.XPathFactoryImpl,你必須設置的Xerces默認的XPathFactory與XPathFactory.newInstance()

然後,因爲SAXON不處理的實體不擴大(錯誤「在DOM!5不支持節點類型」)

+0

我嘗試過使用這種方法,但是當使用文檔作爲DOM源時,您會得到'[致命錯誤]:xxx:yyy:字符引用「&#' – 2015-04-29 14:09:50

+0

您可以給出更多源代碼的詳細信息,XML輸入嗎? – jguiraud 2015-05-01 12:31:34

+0

實際上我沒有找到另一種處理我的需求的方法,它將實體數據保存在一個不會被翻譯的屬性中。 – 2015-05-01 14:30:53

1

我使用這個解決方案,它運作良好:

<xsl:variable name="prolog" select="substring-before(unparsed-text(document-uri(.)),'&lt;root')"/> 

<xsl:template match="/"> 
    <xsl:value-of select="$prolog" disable-output-escaping="yes"/> 
    <xsl:apply-templates/> 
</xsl:template> 
+0

我還沒有嘗試過,但它看起來只會保留序言;實體引用仍然會被擴展,我可以看到使用'xsl:analyze-string'來分析序言,並在鍵/值對中構建一個結構(或映射爲3.0),然後在處理過程中替換它們。 (爲這個想法+1)爲了實際解決這個問題,我最終編寫了一個類似於另一個答案中提到的「lexev」java程序的Omnimark程序。 – 2017-03-14 18:05:37