我有一个巨大的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非常大,如何实现它。
答案 0 :(得分:4)
一种方法是使用文档对象模型(DOM),回溯到此,顾名思义,它需要将整个文档加载到内存中,而Java的DOM API非常需要内存。好处是,您可以利用XPath来查找有问题的节点
仔细查看Java API for XML Processing (JAXP) 以获取更多详细信息和其他API
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new File("..."));
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression xExpress = xPath.compile("/orgs/org/item[text()='b']");
NodeList nodeList = (NodeList) xExpress.evaluate(doc.getDocumentElement(), XPathConstants.NODESET);
好的,这不是应该的那么简单。删除节点可能会在文档中留下空白区域,这对于清理来说“很好”。下面的方法是一个简单的库方法我改编自我发现的一些互联网代码,它将删除指定的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);
}
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);
}
}