如何从XML中删除不需要的标记

时间:2015-01-16 06:03:30

标签: java xml xml-parsing

我有一个巨大的XML,我想从中删除不需要的标签。 EX'。

<orgs>
    <org name="Test1">
        <item>a</item>
        <item>b</item>
    </org>
    <org name="Test2">
        <item>c</item>
        <item>b</item>
        <item>e</item>
    </org>
</orgs>

我想从此xml中删除所有<item>b</item>。哪个解析器api应该用于此,因为xml非常大,如何实现它。

3 个答案:

答案 0 :(得分:4)

一种方法是使用文档对象模型(DOM),回溯到此,顾名思义,它需要将整个文档加载到内存中,而Java的DOM API非常需要内存。好处是,您可以利用XPath来查找有问题的节点

仔细查看Java API for XML Processing (JAXP) 以获取更多详细信息和其他API

步骤:1加载文档

DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new File("..."));

设置2:查找违规节点

XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression xExpress = xPath.compile("/orgs/org/item[text()='b']");
NodeList nodeList = (NodeList) xExpress.evaluate(doc.getDocumentElement(), XPathConstants.NODESET);

设置3:删除违规节点

好的,这不是应该的那么简单。删除节点可能会在文档中留下空白区域,这对于清理来说“很好”。下面的方法是一个简单的库方法我改编自我发现的一些互联网代码,它将删除指定的Node,但也会删除任何空格/文本节点

public static void removeNode(Node node) {
    if (node != null) {
        while (node.hasChildNodes()) {
            removeNode(node.getFirstChild());
        }

        Node parent = node.getParentNode();
        if (parent != null) {
            parent.removeChild(node);
            NodeList childNodes = parent.getChildNodes();
            if (childNodes.getLength() > 0) {
                List<Node> lstTextNodes = new ArrayList<Node>(childNodes.getLength());
                for (int index = 0; index < childNodes.getLength(); index++) {
                    Node childNode = childNodes.item(index);
                    if (childNode.getNodeType() == Node.TEXT_NODE) {
                        lstTextNodes.add(childNode);
                    }
                }
                for (Node txtNodes : lstTextNodes) {
                    removeNode(txtNodes);
                }
            }
        }
    }
}

在有问题的节点上循环......

for (int index = 0; index < nodeList.getLength(); index++) {
    Node node = nodeList.item(index);
    removeNode(node);
}

第4步:保存结果

Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.setOutputProperty(OutputKeys.INDENT, "yes");
tf.setOutputProperty(OutputKeys.METHOD, "xml");
tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

DOMSource domSource = new DOMSource(doc);
StreamResult sr = new StreamResult(System.out);
tf.transform(domSource, sr);

其中输出类似......

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<orgs>
  <org name="Test1">
    <item>a</item>
  </org>
  <org name="Test2">
    <item>c</item>
    <item>e</item>
  </org>
</orgs>

答案 1 :(得分:3)

执行此操作的标准方法是使用XSLT。您需要一个包含两个规则的样式表:一个可以复制未更改内容的标识规则:

<xsl:template match="*">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

以及删除不需要的元素的第二条规则:

<xsl:template match="item[. = 'b']"/>

与基于DOM的方法一样,如果您的文档太大而无法进入内存,则可能会出现问题。在XSLT 3.0中,您可以使用流式传输解决此问题。 XSLT 3.0也使得#34; identity&#34;转换更容易编写,因此现在整个代码变为:

<xsl:transform version="3.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:mode streamable="yes" on-no-match="shallow-copy"/>
  <xsl:template match="item[. = 'b']"/>
</xsl:transform>

答案 2 :(得分:0)

如果您的数据不适合您的内存,则需要pull parser一次不加载文件。 如果您的数据适合内存,那么使用data projection(我与之关联的项目)的解决方案非常简短:

public class RemoveTags {

    public interface Projection {
        @XBDelete("//item[text()='b']")
        void deleteAllItems();
    }

    public static void main(String[] args) throws IOException {
        XBProjector projector = new XBProjector();
        Projection projection = projector.io().file("data.xml").read(Projection.class);
        projection.deleteAllItems();
        projector.io().file("withoutItems.xml").write(projection);
    }

}