如何从XML文件中删除额外的空行?

20

简而言之,我有一个XML文件中生成了许多空行,我正在寻找一种方法来删除它们以清理文件。如何做到这一点?

详细解释如下:我目前有这个XML文件:

<recent>
  <paths>
    <path>path1</path>
    <path>path2</path>
    <path>path3</path>
    <path>path4</path>
  </paths>
</recent>

我使用以下Java代码删除所有的<br>标签,并替换成新的:

public void savePaths( String recentFilePath ) {
    ArrayList<String> newPaths = getNewRecentPaths();
    Document recentDomObject = getXMLFile( recentFilePath );  // Get the <recent> element.
    NodeList pathNodes = recentDomObject.getElementsByTagName( "path" );   // Get all <path> nodes.

    //1. Remove all old path nodes :
        for ( int i = pathNodes.getLength() - 1; i >= 0; i-- ) { 
            Element pathNode = (Element)pathNodes.item( i );
            pathNode.getParentNode().removeChild( pathNode );
        }

    //2. Save all new paths :
        Element pathsElement = (Element)recentDomObject.getElementsByTagName( "paths" ).item( 0 );   // Get the first <paths> node.

        for( String newPath: newPaths ) {
            Element newPathElement = recentDomObject.createElement( "path" );
            newPathElement.setTextContent( newPath );
            pathsElement.appendChild( newPathElement );
        }

    //3. Save the XML changes :
        saveXMLFile( recentFilePath, recentDomObject ); 
}

执行该方法多次后,我得到了一个XML文件,其中包含正确的结果,但在"paths"标签之后和第一个"path"标签之前有许多空行,如下所示:

<recent>
  <paths>





    <path>path5</path>
    <path>path6</path>
    <path>path7</path>
  </paths>
</recent>

有人知道如何修复这个问题吗?

------------------------------------------- 编辑:添加getXMLFile(...),saveXMLFile(...)代码。

public Document getXMLFile( String filePath ) { 
    File xmlFile = new File( filePath );

    try {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document domObject = db.parse( xmlFile );
        domObject.getDocumentElement().normalize();

        return domObject;
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

public void saveXMLFile( String filePath, Document domObject ) {
    File xmlOutputFile = null;
    FileOutputStream fos = null;

    try {
        xmlOutputFile = new File( filePath );
        fos = new FileOutputStream( xmlOutputFile );
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
        transformer.setOutputProperty( "{http://xml.apache.org/xslt}indent-amount", "2" );
        DOMSource xmlSource = new DOMSource( domObject );
        StreamResult xmlResult = new StreamResult( fos );
        transformer.transform( xmlSource, xmlResult );  // Save the XML file.
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (TransformerConfigurationException e) {
        e.printStackTrace();
    } catch (TransformerException e) {
        e.printStackTrace();
    } finally {
        if (fos != null)
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}

看一下你的 saveXMLFile 方法的内容可能会有帮助。 - Markus
2
你可以查看使用Java删除XML中的节点和空行和https://dev59.com/sFrUa4cB1Zd3GeqPlqCF。 - MadProgrammer
11个回答

29
首先,解释一下为什么会发生这种情况——可能会有点偏离,因为您没有包含用于将XML文件加载到DOM对象中的代码。 当您从文件中读取XML文档时,标记之间的空格实际上构成了有效的DOM节点,根据DOM规范。因此,XML解析器将每个这样的空格序列视为DOM节点(类型为TEXT);
要摆脱它,我可以想到三种方法:
1.将XML与模式相关联,然后在DocumentBuilderFactory上使用setValidating(true)和setIgnoringElementContentWhitespace(true)。
(注意:如果解析器处于验证模式下,setIgnoringElementContentWhitespace才能正常工作,这就是为什么必须使用setValidating(true)的原因)
2.编写一个XSL来处理所有节点,过滤掉仅包含空格的TEXT节点。
3.使用Java代码来实现:使用XPath查找所有仅包含空格的TEXT节点,迭代遍历它们并将每个节点从其父级中删除(使用getParentNode().removeChild())。像这样的东西就可以了(doc将是您的DOM文档对象):
XPath xp = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xp.evaluate("//text()[normalize-space(.)='']", doc, XPathConstants.NODESET);

for (int i=0; i < nl.getLength(); ++i) {
    Node node = nl.item(i);
    node.getParentNode().removeChild(node);
}

我不知道怎么做 :), 但我已经将getXMLFile(...)代码添加到问题中。 - Brad
另一种可能性是定义一个XML模式来验证文档,然后使用DocumentBuilderFactory的“setIgnoringElementContentWhitespace”与“setValidating”相结合。有很多方法可以解决这个问题。 - Isaac
如何删除<p>标签内的换行符,例如:<p id="P2">细胞色素P450还原酶(NADPH-细胞色素P450氧还酶;EC 1.6.2.4;缩写为POR或CPR)是细胞色素P450(P450)超家族异物代谢酶的关键电子供体。它还在内源代谢中发挥着许多重要作用,将电子传递给一系列受体,包括细胞色素b5(支持脂肪酸脱饱和酶和延长酶活性),角鲨烷单加氧酶(甾体生物合成。</p> - Rajendra_Prasad

5
我能够通过删除所有旧的“path”节点后使用以下代码来解决此问题:
while( pathsElement.hasChildNodes() )
    pathsElement.removeChild( pathsElement.getFirstChild() );

这将删除XML文件中生成的所有空白。

特别感谢MadProgrammer提供的有用链接评论。


5
我不会盲目地删除子节点,而不知道它们是什么。至少,在这里我会包含一个测试来确保我确实正在删除一个空文本节点(使用'getNodeType'和'getNodeValue')。 - Isaac
@Isaac..我同意你的观点,但在我的情况下,我确定它们都是空的,因为我已经自己删除了它们。相反,如果有什么遗漏没有被删除,那么我想要将其删除 :) - Brad
@Brad,请检查我的答案:http://goo.gl/06Qd9,我解释了如何在不盲目删除所有子节点的情况下删除这些空行,并写了一些关于此类行为原因的内容。 - Dmitry Frank

2
您可以查看类似于这个的内容,如果您只需要快速“清理”您的xml。然后,您可以拥有一个如下的方法:
public static String cleanUp(String xml) {
    final StringReader reader = new StringReader(xml.trim());
    final StringWriter writer = new StringWriter();
    try {
        XmlUtil.prettyFormat(reader, writer);
        return writer.toString();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return xml.trim();
}

此外,如果您需要比较和检查差异,可以使用XMLUnit工具。

XmlUtil属于哪个库?请始终提到该库... - SharadxDutta
这是违反XMLUnit目标的行为。该库明确实现了以更好的方式测试生成XML输出的代码。从这个意义上讲,它不应该在生产代码中使用... - Olivier Faucheux

2
我曾经遇到过同样的问题,很长一段时间我都不知道怎么解决。但是现在,在Brad提出问题并回答自己的问题后,我找到了问题所在。
我需要添加自己的答案,因为Brad的答案并不完美,就像Isaac所说的那样:
“我不会盲目删除子节点而不知道它们是什么。”
因此,更好的“解决方案”(引用原话因为它更像是一种变通方法)是:
pathsElement.setTextContent("");

这将完全删除无用的空白行。它肯定比删除所有子节点要好。Brad,这对你也适用。

但是,这是一种效果,而不是原因,我们知道如何消除这种效果,而不是原因。

原因是:当我们调用 removeChild() 时,它会删除此子项,但它会保留已删除子项的缩进和换行符。并且此缩进和换行符被视为文本内容。

因此,要消除原因,我们应确定如何删除子项及其缩进。欢迎访问我的关于此问题的提问


是的,简单多了...假设你确实想要盲目删除所有子节点而不知道它们是什么。 :-) - Luke Usherwood

1
在我的情况下,我将其转换为字符串,然后只需使用正则表达式:
        //save as String
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        tr.transform(new DOMSource(document), result);
        strResult = writer.toString();

        //remove empty lines 
        strResult = strResult.replaceAll("\\n\\s*\\n", "\n");

1
是的,当你需要一个字符串时非常理想。 - WesternGun

1
如果使用DOM处理API(例如DOM4J),有一种非常简单的方法来摆脱空行:
- 将要保留的文本放入变量中(即text)。 - 使用node.setText("")将节点文本设置为""。 - 使用node.setText(text)将节点文本设置为text
完成!不再有空行了。其他答案很好地说明了XML输出中额外的空行实际上是类型为文本的额外节点。
只要更改文本设置函数的名称以适应您的API中的函数,就可以使用此技术与任何DOM解析系统一起使用,因此表示它的方式稍微抽象一些。
希望这有所帮助 :)

1
当我使用dom4j删除一些元素时,遇到了相同的问题,上述解决方案在没有添加其他所需的jar的情况下无用。最终,我找到了一个简单的解决方案,只需要使用JDK io包:
  1. 使用BufferedReader读取xml文件并过滤空行。
StringBuilder stringBuilder = new StringBuilder();
FileInputStream fis = new FileInputStream(outFile);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String s;
while ((s = br.readLine()) != null) {
  if (s.trim().length() > 0) {
    stringBuilder.append(s).append("\n");
  }
}

将字符串写入XML文件。
OutputStreamWriter osw = new OutputStreamWriter(fou);
BufferedWriter bw = new BufferedWriter(osw);
String str = stringBuilder.toString();
bw.write(str);
bw.flush();
  1. 记得关闭所有流

今天我尝试了这个,效果很好。 - Brad

0
非常晚的回答,但也许对某些人仍有帮助。
我在我的类中有这段代码,在转换后构建文档(就像你一样):
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

将最后一行改为

transformer.setOutputProperty(OutputKeys.INDENT, "no");

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0

我正在使用以下代码:

System.out.println("Start remove textnode");
        i=0;
        while (parentNode.getChildNodes().item(i)!=null) {
            System.out.println(parentNode.getChildNodes().item(i).getNodeName());
            if (parentNode.getChildNodes().item(i).getNodeName().equalsIgnoreCase("#text")) {
                parentNode.removeChild(parentNode.getChildNodes().item(i));
                System.out.println("text node removed");
            }
            i=i+1;

        }

0
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringElementContentWhitespace(true);

1
这将不会忽略新生成的 XML 中的空格。已进行测试。 - NeverGiveUp161

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接