2009-06-11 12 views
16

tengo algunos (5.0) código Java que construye un DOM de varios (en caché) fuentes de datos, a continuación, elimina ciertos nodos de elementos que no son necesarios, a continuación, serializa el resultado en una cadena XML usando:Cómo quitar los nodos de texto solo en blanco de un DOM antes de la serialización?

// 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(); 

Sin embargo , ya que estoy eliminando varios nodos de elementos, termino con un montón de espacio en blanco adicional en el documento serializado final.

¿Hay una manera simple de eliminar/colapsar los espacios en blanco extraños del DOM antes (o mientras) se serializa en una Cadena?

Respuesta

31

puede encontrar nodos de texto vacíos utilizando XPath, luego eliminarlos mediante programación de este modo:

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); 
} 

Este enfoque puede ser útil si desea tener más control sobre la eliminación de nodos que lo que se logra fácilmente con una plantilla XSL te

+0

Me gusta esta solución de "solo código" incluso mejor que la solución XSL, y como dijiste hay un poco más de control sobre la eliminación de nodos, si es necesario. –

+2

Por cierto, este método solo parece funcionar si primero llamo a doc.normalize() antes de realizar la eliminación del nodo. No estoy seguro de por qué eso hace la diferencia. –

+3

Excelente respuesta. Funciona para mí incluso sin normalizar(). –

7

Trate de usar la siguiente XSL y el elemento strip-space para serializar el DOM:

<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

Gracias! Esa es una buena respuesta y lo intenté ... y funciona. –

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

Esto retendrá la indentación de xml.

+1

No elimina espacios superfluos. –

4

A continuación, el código elimina los nodos de comentarios y los nodos de texto con todos los espacios vacíos. Si el nodo de texto tiene algún valor, será recortado valor

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

Otro enfoque posible es eliminar la vecina espacios en blanco al mismo tiempo que usted está eliminando los nodos de destino:

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); 
    } 
} 

Esto tiene la ventaja de mantener el resto del formato existente intacto.

0

el siguiente código funciona:

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

Esta respuesta se beneficiaría con alguna explicación. – Eiko

Cuestiones relacionadas