2009-06-11 6 views
16

Ich habe einig Java (5.0) Code, der einen DOM aus verschiedenen (gecached) Datenquellen konstruiert, dann bestimmten Elementknoten entfernt, die nicht benötigt werden, serialisiert dann das Ergebnis in eine XML-Zeichenfolge mit:Wie kann man Textknoten, die nur Leerzeichen enthalten, aus einem DOM vor der Serialisierung entfernen?

// Serialize DOM back into a string 
Writer out = new StringWriter(); 
Transformer tf = TransformerFactory.newInstance().newTransformer(); 
tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 
tf.setOutputProperty(OutputKeys.INDENT, "no"); 
tf.transform(new DOMSource(doc), new StreamResult(out)); 
return out.toString(); 

jedoch Da ich mehrere Elementknoten entferne, habe ich im letzten serialisierten Dokument eine Menge zusätzlicher Leerzeichen.

Gibt es eine einfache Möglichkeit zum Entfernen/Ausblenden der fremden Leerzeichen aus dem DOM vor (oder während) es in einen String serialisiert ist?

Antwort

31

Sie leeren Textknoten mit XPath finden kann, dann entfernen sie programmatisch wie so:

XPathFactory xpathFactory = XPathFactory.newInstance(); 
// XPath to find empty text nodes. 
XPathExpression xpathExp = xpathFactory.newXPath().compile(
     "//text()[normalize-space(.) = '']"); 
NodeList emptyTextNodes = (NodeList) 
     xpathExp.evaluate(doc, XPathConstants.NODESET); 

// Remove each empty text node from document. 
for (int i = 0; i < emptyTextNodes.getLength(); i++) { 
    Node emptyTextNode = emptyTextNodes.item(i); 
    emptyTextNode.getParentNode().removeChild(emptyTextNode); 
} 

Dieser Ansatz kann nützlich sein, wenn Sie mehr Kontrolle über die Knotenentfernung haben möchten, als mit einem XSL-Templa leicht erreicht werden kann te.

+0

Ich mag diese "code only" -Lösung sogar besser als die XSL-Lösung, und wie Sie sagten, gibt es bei Bedarf etwas mehr Kontrolle über die Entfernung von Knoten. –

+2

Übrigens scheint diese Methode nur zu funktionieren, wenn ich zuerst doc.normalize() aufruft, bevor ich den Knoten entferne. Ich bin mir nicht sicher, warum das einen Unterschied macht. –

+3

Ausgezeichnete Antwort. Funktioniert für mich auch ohne normalize(). –

7

Versuchen Sie das folgende XSL und das strip-space Element Ihren DOM serialisiert:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" omit-xml-declaration="yes"/> 

    <xsl:strip-space elements="*"/> 

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

</xsl:stylesheet> 

http://helpdesk.objects.com.au/java/how-do-i-remove-whitespace-from-an-xml-document

+0

Dank! Das ist eine gute Antwort und ich habe es versucht ... und es funktioniert. –

-3
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 

Dies wird beibehalten Xml Einrückung.

+1

Es werden keine überflüssigen Leerzeichen entfernt. –

4

Der folgende Code löscht die Kommentarknoten und Textknoten mit allen leeren Leerzeichen. Wenn der Text-Knoten einen gewissen Wert hat, wird Wert getrimmt werden

public static void clean(Node node) 
{ 
    NodeList childNodes = node.getChildNodes(); 

    for (int n = childNodes.getLength() - 1; n >= 0; n--) 
    { 
    Node child = childNodes.item(n); 
    short nodeType = child.getNodeType(); 

    if (nodeType == Node.ELEMENT_NODE) 
     clean(child); 
    else if (nodeType == Node.TEXT_NODE) 
    { 
     String trimmedNodeVal = child.getNodeValue().trim(); 
     if (trimmedNodeVal.length() == 0) 
      node.removeChild(child); 
     else 
      child.setNodeValue(trimmedNodeVal); 
    } 
    else if (nodeType == Node.COMMENT_NODE) 
     node.removeChild(child); 
    } 
} 

Ref: http://www.sitepoint.com/removing-useless-nodes-from-the-dom/

0

Ein anderer möglicher Ansatz ist benachbarte Leerzeichen zur gleichen Zeit zu entfernen, wie Sie die Zielknoten sind zu entfernen:

private void removeNodeAndTrailingWhitespace(Node node) { 
    List<Node> exiles = new ArrayList<Node>(); 

    exiles.add(node); 
    for (Node whitespace = node.getNextSibling(); 
      whitespace != null && whitespace.getNodeType() == Node.TEXT_NODE && whitespace.getTextContent().matches("\\s*"); 
      whitespace = whitespace.getNextSibling()) { 
     exiles.add(whitespace); 
    } 

    for (Node exile: exiles) { 
     exile.getParentNode().removeChild(exile); 
    } 
} 

Dies hat den Vorteil, dass der Rest der vorhandenen Formatierung intakt bleibt.

0

Der folgende Code funktioniert:

public String getSoapXmlFormatted(String pXml) { 
    try { 
     if (pXml != null) { 
      DocumentBuilderFactory tDbFactory = DocumentBuilderFactory 
        .newInstance(); 
      DocumentBuilder tDBuilder; 
      tDBuilder = tDbFactory.newDocumentBuilder(); 
      Document tDoc = tDBuilder.parse(new InputSource(
        new StringReader(pXml))); 
      removeWhitespaces(tDoc); 
      final DOMImplementationRegistry tRegistry = DOMImplementationRegistry 
        .newInstance(); 
      final DOMImplementationLS tImpl = (DOMImplementationLS) tRegistry 
        .getDOMImplementation("LS"); 
      final LSSerializer tWriter = tImpl.createLSSerializer(); 
      tWriter.getDomConfig().setParameter("format-pretty-print", 
        Boolean.FALSE); 
      tWriter.getDomConfig().setParameter(
        "element-content-whitespace", Boolean.TRUE); 
      pXml = tWriter.writeToString(tDoc); 
     } 
    } catch (RuntimeException | ParserConfigurationException | SAXException 
      | IOException | ClassNotFoundException | InstantiationException 
      | IllegalAccessException tE) { 
     tE.printStackTrace(); 
    } 
    return pXml; 
} 

public void removeWhitespaces(Node pRootNode) { 
    if (pRootNode != null) { 
     NodeList tList = pRootNode.getChildNodes(); 
     if (tList != null && tList.getLength() > 0) { 
      ArrayList<Node> tRemoveNodeList = new ArrayList<Node>(); 
      for (int i = 0; i < tList.getLength(); i++) { 
       Node tChildNode = tList.item(i); 
       if (tChildNode.getNodeType() == Node.TEXT_NODE) { 
        if (tChildNode.getTextContent() == null 
          || "".equals(tChildNode.getTextContent().trim())) 
         tRemoveNodeList.add(tChildNode); 
       } else 
        removeWhitespaces(tChildNode); 
      } 
      for (Node tRemoveNode : tRemoveNodeList) { 
       pRootNode.removeChild(tRemoveNode); 
      } 
     } 
    } 
} 
+0

Diese Antwort würde durch eine Erklärung profitieren. – Eiko

Verwandte Themen