【问题标题】:Remove empty XML elements in Java删除 Java 中的空 XML 元素
【发布时间】:2012-02-24 05:37:57
【问题描述】:

我有一个输出 XML,其中包含空元素和空元素,但具有属性。

我查看了一些较早的帖子,这些帖子帮助我解决了部分问题。

我使用了以下 XSLT 解决方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">   <xsl:template match="@*|node()">
    <xsl:if test=". != '' or ./@* != ''">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:if>   </xsl:template> </xsl:stylesheet>

但问题是删除具有子元素的元素也具有类似属性的元素

<CurrencyList> 
<Currency code="EURO"/> 
<Currency code="USD"/>
</CurrencyList>

有人知道如何解决这个问题吗?

非常感谢

【问题讨论】:

  • 解析 xml 并验证子元素和属性的每个元素不是您的选择吗?
  • @CatalinCiobanu 是的,它可能是
  • 好吧,您可以将元素作为列表 List 获取并询问大小......但我们的同事 Joop Eggen 提出了一个更好(更好)的解决方案 -> 递归!
  • 你想对一个只包含另一个空元素的元素发生什么?应该同时删除还是只删除内部的?
  • @JörnHorstmann 如果只包含空元素,则应该同时删除它

标签: java xml regex xslt


【解决方案1】:

这就像删除空目录:你必须进行深度优先递归遍历:如果所有子目录都被删除,那么可以考虑删除当前目录。

因此,删除最好在 Java 中通过递归来完成。优点是不需要副本。


代码

根据要求,由于使用 XML API 非常零碎,一些未经测试的代码:

import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

public class XmlCleanup {

    public static void main(String[] args) {
        if (args.length == 0) {
            args = new String[] { "/home/joop/Labortablo/test1.xml" };
        }
        new XmlCleanup().process(args[0]);
    }

    public void process(String xmlPath) {
        try {
            // Read XML document:
            DocumentBuilder builder =
                    DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(new File(xmlPath));

            removeEmptyChildElements(doc.getDocumentElement());

            // Write XML document back:
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(new File(xmlPath
                    .replaceFirst("\\.xml$", "") + "-clean.xml"));
            transformer.transform(source, result);
        } catch (TransformerException ex) {
            Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(XmlCleanup.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void removeEmptyChildElements(Element parentElement) {
        List<Element> toRemove = new LinkedList<Element>();

        NodeList children = parentElement.getChildNodes();
        int childrenCount = children.getLength();
        for (int i = 0; i < childrenCount; ++i) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                Element childElement = (Element) child;
                removeEmptyChildElements(childElement);
                if (elementIsRedundant(childElement)) {
                    toRemove.add(childElement);
                }
            }
        }

        for (Element childElement: toRemove) {
            parentElement.removeChild(childElement);
        }
        parentElement.normalize();
    }

    private boolean elementIsRedundant(Element element) {
        if (element.hasAttributes())
            return false;
        if (!element.hasChildNodes())
            return true;
        NodeList children = element.getChildNodes();
        int childrenCount = children.getLength();
        for (int i = 0; i < childrenCount; ++i) {
            Node child = children.item(i);
            String value = child.getNodeValue();
            if (value != null && !value.matches("\\s*")) {
                return false; // Found non-whitespace text
            }
        }
        return true;
    }
}

它使用java.xml.transform,因此您也可以使用 XSLT 转换;更简单一点是使用javax.xml.stream.XMLOutputFactory

【讨论】:

【解决方案2】:

我认为从身份转换开始,您走在了正确的轨道上。我建议保持身份模板不变,然后添加一个忽略空元素的更具体的模板。

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

对“normalize-space”的调用使这个模板折叠所有连续的空白,这通常只用于缩进。然后匹配的第二部分排除所有本身具有属性或具有属性的后代的元素。出于调试目的,我让模板在删除元素时在输出中创建注释。

<xsl:template match="*[normalize-space(.) = '' and not(descendant-or-self::*/@*)]">
    <xsl:comment><xsl:value-of select="name(.)" /></xsl:comment>
</xsl:template>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 2013-05-21
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 2016-06-05
    • 1970-01-01
    相关资源
    最近更新 更多