2013-07-02 51 views
2

我需要一些XSLT(或,這是 - 請參閱下面的內容)用替代字符替換所有屬性中的換行符。使用XSLT替換XML屬性中的換行符

我有處理舊XML存儲所有數據的屬性,並使用新的線來表達基數。例如:

<sample> 
    <p att="John 
    Paul 
    Ringo"></p> 
</sample> 

這些新線正在用空格代替,當我解析Java中的文件(根據XML規範),但我希望把他們當作一個列表,所以這種行爲是不特別有用。

我的「解決方案」是使用XSLT來取代所有換行符在與其他一些分隔符的所有屬性 - 但我有XSLT的零知識。到目前爲止,我所見過的所有例子都是非常具體的,或者取代了節點內容而不是屬性值。

我已經涉足與XSLT 2.0的replace()但我有一個很難把一切融合在一起。

XSLT甚至是正確的解決方案嗎?與下面的XSLT:

<xsl:template match="sample/*"> 
    <xsl:for-each select="@*"> 
     <xsl:value-of select="replace(current(), '\n', '|')"/> 
    </xsl:for-each> 
</xsl:template> 
應用到示例XML

輸出以下使用撒克遜:

John Paul Ringo 

顯然,這種格式是不是以後我 - 這僅僅是與replace()實驗 - 但是到了XSLT處理的時候,換行符已經被標準化了嗎?如果是這樣,是否有任何其他方式來解析這些值作爲使用Java解析器令狀?迄今爲止我只使用JAXB。

+0

我有一個非常討厭的感覺,我可能需要戴上我的橡膠手套,並在解析之前在XML字符串上實現一個骯髒的正則表達式。不幸的是,我無法控制正在生成的XML。 – nullPainter

+0

其實不,這太可怕了,不能考慮。 – nullPainter

+0

如果屬性值中的空白在語義上很重要,那麼您不會處理XML,而需要使用非XML工具來處理它。 [根據規範](http://www.w3.org/TR/xml/#AVNormalize)屬性值中的所有換行符_must_必須由解析器轉換爲空格,並且如果您希望在您看到的值中包含換行符解析後,它必須作爲字符引用轉義(' ') –

回答

1

我用JSoup(這是對@Ian Roberts有關使用非XML工具解析XML的評論)的預處理來解決(ish)問題。 JSoup是(或曾經)爲HTML文檔設計的,但在這種情況下效果很好。

我的代碼如下:

@Test 
public void verifyNewlineEscaping() { 
    final List<Node> nodes = Parser.parseXmlFragment(FileUtils.readFileToString(sourcePath.toFile(), "UTF-8"), ""); 

    fixAttributeNewlines(nodes); 

    // Reconstruct XML 
    StringBuilder output = new StringBuilder(); 
    for (Node node : nodes) { 
     output.append(node.toString()); 
    } 

    // Print cleansed output to stdout 
    System.out.println(output); 
} 

/** 
* Replace newlines and surrounding whitespace in XML attributes with an alternative delimiter in 
* order to avoid whitespace normalisation converting newlines to a single space. 
* 
* <p> 
* This is useful if newlines which have semantic value have been incorrectly inserted into 
* attribute values. 
* </p> 
* 
* @param nodes nodes to update 
*/ 
private static void fixAttributeNewlines(final List<Node> nodes) { 

    /* 
    * Recursively iterate over all attributes in all nodes in the XML document, performing 
    * attribute string replacement 
    */ 
    for (final Node node : nodes) { 
     final List<Attribute> attributes = node.attributes().asList(); 

     for (final Attribute attribute : attributes) { 

      // JSoup reports whitespace as attributes 
      if (!StringUtils.isWhitespace(attribute.getValue())) { 
       attribute.setValue(attribute.getValue().replaceAll("\\s*\r?\n\\s*", "|")); 
      } 
     } 

     // Recursively process child nodes 
     if (!node.childNodes().isEmpty()) { 
      fixAttributeNewlines(node.childNodes()); 
     } 
    } 
} 

對於我的問題的示例XML,這個方法的輸出是:

<sample> 
    <p att="John|Paul|Ringo"></p> 
</sample> 

請注意,我沒有使用&#10;因爲JSoup比較警惕其字符逃逸一切屬性值。它也用它們的UTF-8等價物取代了現有的數字實體引用,所以時間會告訴你這是否是一個可以通過的解決方案。

+1

請注意,使用JSoup的缺點是它當前將屬性名稱轉換爲小寫。有一個[open bug](https://github.com/jhy/jsoup/issues/272)詳細說明了這一點。 – nullPainter

2

這似乎很難做到這一點。正如我在Are line breaks in XML attribute values allowed?中發現的那樣 - 屬性中的新行字符是有效的,但XML解析器將其規範化(https://stackoverflow.com/a/8188290/1324394),因此它在處理之前(以及在替換之前)可能會丟失。它已被XML解析器,將已經完成了屬性值規範化處理後

+0

我也看到了,但我希望他們仍然會在那裏進行一些XSLT修復。 我自那時以後發現http://jdom.org/不是自稱是XML解析器的問題,它解決了這個問題,這大概解除了遵守XML規範的必要。現在就來試試吧...... – nullPainter

+0

只要大聲想一想,你可以這樣做'replace(/ data/@ value,'\ s {2,10}','|')' - 這不是絕對的正確的,因爲它依賴於將有多個空格而不是換行符,但它可以創造一份工作。 –

+0

@JirkaŠ。否,那是行不通的,因爲在數據達到XPath數據模型之前,XML解析器會將屬性值中的所有連續空白摺疊爲單個空間。 –

0

XSLT只看到XML。

我認爲一些XML解析器有一個選項,以抑制屬性值正常化。如果您無法訪問這樣的解析器,我認爲在解析之前用文本替換(\ r?\ n)&#x0A;可能是您最好的逃生路線。以這種方式轉義的換行符不會受到屬性值標準化的影響。

+0

謝謝邁克爾。在做了合理數量的挖掘之後,我想出了空白試圖找到一個基於Java的解析器,它允許抑制屬性值標準化。原文替換很困難,因爲我無法控制正在生成的XML。這意味着我無法將替換限制爲屬性值。 – nullPainter