2009-08-22 69 views
5

給定一個w3c DOM(特別是Java的默認實現),如何更改該DOM中每個元素/屬性/節點的名稱空間?有效地,最好。 DOM似乎沒有setNamespaceURI方法,這是不方便的。如何更改DOM中每個節點上的命名空間?

我已經嘗試過XSL方法,但是他們在JAXP變換器中工作失敗(儘管他們在Saxon9B中正常工作,但由於各種其他原因無法使用)。

基本上,我需要一個純粹的核心Java解決方案,它將允許我接收一個文檔並更改其命名空間。

+0

XSL也許是最簡單的解決方案,並應工作在JAXP。你嘗試了什麼,它是如何失敗的? – skaffman 2009-09-29 22:25:31

回答

3

基於我極度偏見的觀點,你想要的將是一個巨大的屁股疼痛。我可以看到類型轉換地獄和衆多遞歸循環需要做到這一點已經可靠了!啊,Java的默認實現,我如何討厭你的NPE:在內部,反向邏輯,簡單操作所需的額外步驟!

所以,是的,我的建議是針對每種可能的節點類型進行類型轉換的循環遞歸,基於我個人的經驗,Java的默認實現很糟糕。

+0

我認爲這裏的其他一些解決方案更優雅,但這就是我最終做的:) – 2009-08-23 17:25:19

+0

雖然有點有趣,但這實際上不應該被接受的答案...... – 2012-07-12 13:33:22

9

這對於支持名稱空間的DOM沒有效率。您必須在每個想要更改名稱空間的後代元素上使用DOM Level 3 Core方法Document.renameNodejavadoc)。 (您通常不需要更改這麼多的Attr節點,因爲沒有前綴的Attr節點的名稱空間始終爲空,而不是元素的名稱空間。)

如果您只想將一個名稱空間替換爲另一方面,使用名稱空間不知道的DOM可能會更快,並且只需更改相關的xmlns屬性即可。您應該能夠通過將DOMConfiguration'namespaces'參數設置爲false來獲得不支持命名空間的DOM,但是我沒有在Java中嘗試過這種方法,這是一種晦澀的小東西,DOM imp會出錯。

2

如果意圖只是更改名稱空間,那麼只需使用一些流編輯器將NS映射更改爲URL即可。

Namspace或多或少是名稱空間前綴和URI之間的綁定。爲了迅速改變命名空間,只需更改映射:

前: 的xmlns:myns名字= 「我的名稱空間URI」

後: 的xmlns:myns名字= 「我的新名稱空間URI」

如果意圖僅僅是改變命名空間,基本上改變映射就足夠了。此外,如果XML文檔具有默認名稱空間,那麼更改默認名稱空間URL值將會更改整個文檔的命名空間。

之前: 的xmlns =「我的名稱空間URI」

後: 的xmlns =「我的新名稱空間URI」

+0

+1純Java實現 – 2009-08-23 13:28:51

+0

尼斯的想法,但此時需要重新序列化爲字節流並重新解析,這從性能角度來看是不可接受的。我已經有一個DOM,我必須使用它。 – 2009-08-23 17:28:05

0

該命名空間的每一個元素上更改沒有定義命名空間前綴通過將targetnamespace屬性應用於根元素。這樣做還需要您使用命名空間前綴來更改每個元素。您可以手動更改此前綴或編寫一些腳本邏輯,讓您的DOM樹只在必要時才能應用。

這裏是關於targetNamespace屬性和nonamespaceschema屬性更多閱讀:

http://www.xml.com/pub/a/2000/11/29/schemas/part1.html?page=8 http://www.computerpoweruser.com/editorial/article.asp?article=articles%2Farchive%2Fc0407%2F48c07%2F48c07.asp

2

我怎樣才能給出一個W3C DOM(Java的默認實現,特別是)改變的每個命名空間該DOM中的元素/屬性/節點?有效地,最好。

我不認爲有一個有效的解決方案也是健壯的。你不能只在根元素上重命名一些東西。考慮到這些文件:

文檔1

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="urn:all" xmlns:f="urn:fleet" xmlns:m="urn:mission"> 
    <f:starfleet> 
    <m:bold> 
     <f:ship name="Enterprise" /> 
    </m:bold> 
    </f:starfleet> 
</root> 

文檔2

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="urn:all"> 
    <starfleet xmlns="urn:fleet"> 
    <bold xmlns="urn:mission"> 
     <ship xmlns="urn:fleet" name="Enterprise" /> 
    </bold> 
    </starfleet> 
</root> 

文檔3

<?xml version="1.0" encoding="UTF-8"?> 
<r:root xmlns:r="urn:all"> 
    <r:starfleet xmlns:r="urn:fleet"> 
    <r:bold xmlns:r="urn:mission"> 
     <r:ship xmlns:r="urn:fleet" name="Enterprise" /> 
    </r:bold> 
    </r:starfleet> 
</r:root> 

這三個文件是在一個命名空間感知的DOM等價的。您可以針對其中的任何一個運行相同的namespaced XPath queries

由於DOM允許您精確指定節點應該如何命名空間,所以沒有全面的一步調用來更改命名空間。您需要走DOM,不僅考慮前綴和URI值,而且還要考慮它們在任何給定時間的scope

這XSLT可以與Transformer可以用來改變的命名空間urn:fleet元素被命名空間作爲urn:new

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:f="urn:fleet" version="1.0"> 
    <xsl:output method="xml" indent="yes" /> 
    <xsl:template match="*"> 
    <xsl:copy> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:copy> 
    </xsl:template> 
    <xsl:template match="f:*"> 
    <xsl:variable name="var.foo" select="local-name()" /> 
    <xsl:element namespace="urn:new" name="{$var.foo}"> 
     <xsl:copy-of select="@*" /> 
     <xsl:apply-templates /> 
    </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

注意事項:進一步的調整將被要求處理的命名空間屬性;懸掛urn:fleet聲明可以留下,這是凌亂的,但很大程度上無關緊要;可能其他的東西,我沒有想到。

+0

這就是我最初使用的,幾乎一字不變,但事實證明,我們客戶對XML的使用是......原始的。或者愚蠢。你選。爲了基督的緣故,如果XML沒有正確放置,他們的工具就會中斷。 – 2009-08-23 17:26:26

0

您可以將您的DOM樹複製到另一棵樹,並在處理過程中進行一些調整。 例如,使用org.apache.xml.utils.DOMBuilder爲ContentHandler的實施,可以覆蓋這樣的方式方法:

public void startElement(String ns, String localName, String name, Attributes atts) throws SAXException { 
     super.startElement("new_namespace", localName, name, atts); 
    } 

DOMBuilder將複製留給你唯一的命名空間替換邏輯中處理所有的髒活。

0

如果確定與使用Xerces類,你可以創建一個的DOMParser替換屬性和元素的URI與你搞掂的URI:

import org.apache.xerces.parsers.DOMParser; 

public static class MyDOMParser extends DOMParser { 
    private Map<String, String> fixupMap = ...; 

    @Override 
    protected Attr createAttrNode(QName attrQName) 
    { 
     if (fixupMap.containsKey(attrQName.uri)) 
      attrQName.uri = fixupMap.get(attrQName.uri); 
     return super.createAttrNode(attrQName); 
    } 

    @Override 
    protected Element createElementNode(QName qName) 
    { 
     if (fixupMap.containsKey(qName.uri)) 
      qName.uri = fixupMap.get(qName.uri); 
     return super.createElementNode(qName); 
    }  
} 

的其他地方,你可以解析爲一個DOM文檔:

DOMParse p = new MyDOMParser(...); 
p.parse(new InputSource(inputStream)); 
Document doc = p.getDocument(); 
0

給出DOM文檔的此代碼將返回一個新的DOM文檔,其中已經應用了一組給定的名稱空間URI轉換(uriMap)。這些鍵必須是源文檔中的URI,這些值是目標文檔中的替換URI。未知名稱空間URI通過不變。它知道改變的xmlns的值:*屬性,但不會改變,可能碰巧有空間URI作爲其值的其他屬性(例如XSD targetNamespace的)

private static Node makeClone(Node kid, Node to, Map<String, String> uriMap) { 
    Document doc = to.getNodeType() == Node.DOCUMENT_NODE ? 
      (Document) to : 
      to.getOwnerDocument(); 
    if (kid.getNodeType() == Node.ELEMENT_NODE) { 
     String newURI = 
       uriMap.containsKey(kid.getNamespaceURI()) ? 
       uriMap.get(kid.getNamespaceURI()) : 
       kid.getNamespaceURI(); 
     Element clone = doc.createElementNS(newURI, kid.getNodeName()); 
     to.appendChild(clone); 
     for (int i = 0; i < kid.getAttributes().getLength(); i++) { 
     Attr attr = (Attr) kid.getAttributes().item(i); 
     String newAttrURI = 
       uriMap.containsKey(attr.getNamespaceURI()) ? 
       uriMap.get(attr.getNamespaceURI()) : 
       attr.getNamespaceURI(); 
     String newValue = attr.getValue(); 
     if (attr.getNamespaceURI() != null && 
       attr.getNamespaceURI().equals(
       "http://www.w3.org/2000/xmlns/") && 
       uriMap.containsKey(attr.getValue())) 
      newValue = uriMap.get(attr.getValue()); 
     clone.setAttributeNS(newAttrURI, attr.getNodeName(), newValue); 
     } 
     return clone; 
    } 
    Node clone = kid.cloneNode(false); 
    doc.adoptNode(clone); 
    to.appendChild(clone); 
    return clone; 
} 

private static void copyKidsChangingNS(Node from, Node to, 
     Map<String, String> uriMap) { 
    NodeList kids = from.getChildNodes(); 
    for (int i = 0; i < kids.getLength(); i++) { 
     Node kid = kids.item(i); 
     Node clone = makeClone(kid, to, uriMap); 
     copyKidsChangingNS(kid, clone, uriMap); 
    } 
} 

public static Document changeDocNS(Document doc, Map<String, String> uriMap) 
     throws Exception { 
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
    dbf.setNamespaceAware(true); 
    DocumentBuilder db = dbf.newDocumentBuilder(); 
    Document newDoc = db.newDocument(); 
    copyKidsChangingNS(doc, newDoc, uriMap); 
    return newDoc; 
} 
相關問題