代码之家  ›  专栏  ›  技术社区  ›  Marc Novakowski

如何在序列化之前从一个dom中去掉仅空白的文本节点?

  •  17
  • Marc Novakowski  · 技术社区  · 16 年前

    我有一些Java(5)代码,它从各种(缓存的)数据源构建DOM,然后删除某些不需要的元素节点,然后使用以下结果将结果序列化为XML字符串:

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

    但是,由于我要删除几个元素节点,最终在最终的序列化文档中会有大量额外的空白。

    在将外部空白序列化为字符串之前(或在将其序列化为字符串时),是否有一种简单的方法从DOM中移除/折叠这些空白?

    6 回复  |  直到 9 年前
        1
  •  33
  •   Evan Fosmark    16 年前

    您可以使用xpath查找空文本节点,然后以编程方式删除它们,如下所示:

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

    如果您想要比XSL模板更容易实现的对节点删除的控制,这种方法可能会很有用。

        2
  •  7
  •   james.garriss Pavel    9 年前

    尝试使用以下XSL和 strip-space 要序列化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

        3
  •  4
  •   Venkata Raju    12 年前

    下面的代码删除注释节点和带有所有空格的文本节点。如果文本节点具有某个值,则将修剪该值。

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

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

        4
  •  0
  •   pimlottc    10 年前

    另一种可能的方法是在删除目标节点的同时删除相邻的空白:

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

    这样做的好处是保持现有格式的其余部分不变。

        5
  •  0
  •   user6615071    9 年前

    以下代码有效:

    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);
                }
            }
        }
    }
    
        6
  •  -3
  •   Jérôme Verstrynge    13 年前
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    

    这将保留XML缩进。