我想从XML元素中删除空节点。 这个xml是从供应商生成的,我没有xml生成的控制权。但由于XML几乎没有空节点,我需要递归删除这些空节点。
这个xml来自OMElement,我使用[XMLUtils] [1]从这个对象获取一个元素 示例XML
<A>
<B>
<C>
<C1>
<C11>something</C11>
<C12>something</C12>
</C1>
</C>
<D>
<D1>
<D11>
<D111 operation="create">
<Node>something else</Node>
</D11>
</D11>
</D1>
<D2>
<D21>
</D21>
</D2>
</D>
</B>
</A>
由于D21是一个空节点,我想删除D21,因为现在D2是一个空节点,我想删除D2,但由于D有D1,我不想删除D.
同样有可能我得到
<A>
<B>
<C>
</C>
</B>
</A>
现在因为C是空的,我想删除C然后是B然后最终删除节点A. 我正在尝试使用Node
中的removeChild()方法执行此操作但到目前为止我无法递归删除它们。 有什么建议可以递归删除它们吗?
我递归地尝试获取节点和节点长度。但节点长度没有帮助
if(childNode.getChildNodes().getLength() == 0 ){
childNode.getParentNode().removeChild(childNode);
}
问候
Dheeraj Joshi
答案 0 :(得分:4)
我没有足够的代表来评论@Adam的解决方案,但是我遇到了一个问题,即在删除节点后,该节点的最后一个兄弟被移动到索引零,导致它不能完全删除空元素。解决方法是使用列表来保存我们想要递归调用以删除的所有节点。
此外,还有一个错误删除了具有属性的空元素。
解决这两个问题:
public static void removeEmptyNodes(Node node) {
NodeList list = node.getChildNodes();
List<Node> nodesToRecursivelyCall = new LinkedList();
for (int i = 0; i < list.getLength(); i++) {
nodesToRecursivelyCall.add(list.item(i));
}
for(Node tempNode : nodesToRecursivelyCall) {
removeEmptyNodes(tempNode);
}
boolean emptyElement = node.getNodeType() == Node.ELEMENT_NODE
&& node.getChildNodes().getLength() == 0;
boolean emptyText = node.getNodeType() == Node.TEXT_NODE
&& node.getNodeValue().trim().isEmpty();
if (emptyElement || emptyText) {
if(!node.hasAttributes()) {
node.getParentNode().removeChild(node);
}
}
}
答案 1 :(得分:3)
这样做,只需创建一个“深入”的递归函数,然后在“备份树”的路上删除空节点,这将同时删除D21和D2。
public static void main(String[] args) throws Exception {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
String input = "<A><B><C><C1><C11>something</C11><C12>something</C12></C1></C><D><D1><D11><D111 operation=\"create\"><Node>something else</Node></D111></D11></D1><D2><D21></D21></D2></D></B></A>";
Document document = builder.parse(new InputSource(new StringReader(
input)));
removeNodes(document);
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
transformer.transform(new DOMSource(document), result);
System.out.println(result.getWriter().toString());
}
public static void removeNodes(Node node) {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
removeNodes(list.item(i));
}
boolean emptyElement = node.getNodeType() == Node.ELEMENT_NODE
&& node.getChildNodes().getLength() == 0;
boolean emptyText = node.getNodeType() == Node.TEXT_NODE
&& node.getNodeValue().trim().isEmpty();
if (emptyElement || emptyText) {
node.getParentNode().removeChild(node);
}
}
输出
<A>
<B>
<C>
<C1>
<C11>something</C11>
<C12>something</C12>
</C1>
</C>
<D>
<D1>
<D11>
<D111 operation="create">
<Node>something else</Node>
</D111>
</D11>
</D1>
</D>
</B>
</A>
答案 2 :(得分:2)
在DOM的顶级元素上使用getTextContent()
。如果方法返回空字符串或null,则可以删除此节点,因为此节点和所有子节点为空。如果方法getTextContent()
不返回空字符串,请在当前节点的每个子节点上调用getTextContent
,依此类推。
请参阅documentation。
答案 3 :(得分:0)
public class RemoveEmprtElement {
public static void main(String[] args) {
ReadFile readFile =new ReadFile();
String strXml=readFile.readFileFromPath(new File("sampleXml4.xml"));
RemoveEmprtElement elementEmprtElement=new RemoveEmprtElement();
DocumentBuilder dBuilder = null;
Document doc = null;
try {
dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
doc = dBuilder.parse(new ByteArrayInputStream(strXml.getBytes()));
elementEmprtElement.getEmptyNodes(doc);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
StreamResult result = new StreamResult(new StringWriter());
trans.transform(new DOMSource(doc), result);
System.out.println(result.getWriter().toString());
}catch(Exception e) {
e.printStackTrace();
}
}
private void getEmptyNodes(Document doc){
try {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//*[not(*)]");
Object resultNS = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) resultNS;
for(int i =0 ; i < nodes.getLength() ; i++){
Node node = nodes.item(i);
boolean emptyElement = node.getNodeType() == Node.ELEMENT_NODE
&& node.getChildNodes().getLength() == 0;
boolean emptyText = node.getNodeType() == Node.TEXT_NODE
&& node.getNodeValue().trim().isEmpty();
if (emptyElement || emptyText) {
xmlNodeRemove(doc,findPath(node));
getEmptyNodes(doc);
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
private void xmlNodeRemove(Document doc,String xmlNodeLocation){
try {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile(xmlNodeLocation);
Object resultNS = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) resultNS;
Node node =nodes.item(0);
if(node!=null && node.getParentNode()!=null && node.getParentNode().hasChildNodes()){
node.getParentNode().removeChild(node);
}
}catch(Exception e) {
e.printStackTrace();
}
}
private String findPath(Node n) {
String path="";
if(n==null){
return path;
}else if(n.getNodeName().equals("#document")){
return "";
}
else{
path=n.getNodeName();
path=findPath(n.getParentNode())+"/"+path;
}
return path;
}
}
答案 4 :(得分:0)
只需使用字符串:
Pattern emptyValueTag = Pattern.compile("\\s*<\\w+/>");
Pattern emptyTagMultiLine = Pattern.compile("\\s*<\\w+>\n*\\s*</\\w+>");
xml = emptyValueTag.matcher(xml).replaceAll("");
while (xml.length() != (xml = emptyTagMultiLine.matcher(xml).replaceAll("")).length()) {
}
return xml;