【问题标题】:Deleting Nodes found with XPath删除使用 XPath 找到的节点
【发布时间】:2015-06-30 11:46:48
【问题描述】:

在用户从 JTable 中选择并删除 XML 文档后,我试图从 XML 文档中删除它。 XML 文件看起来像这样,并且有多个同名标签,但轨道由轨道数据之前的带有整数的标签标识:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Tracks</key>
    <dict>
        <key>203</key>
        <dict>
            <key>Track ID</key><integer>203</integer>
            <key>Name</key><string>Winter</string>
            <key>Artist</key><string>Daughter</string>
            <!-- etc -->
            <key>Play Count</key><integer>2</integer>
            <key>Skip Count</key><integer>1</integer>
            <key>Track Type</key><string>File</string>
        </dict>
        <key>204</key>
        <dict>
            <!-- Another set of track data, etc... -->
        </dict>
    </dict>
</dict>
</plist>

我将此 trackID 传递给当前正在使用 XPath 查找 &lt;key&gt;...&lt;/key&gt; 和以下 &lt;dict&gt;...&lt;/dict&gt; 的方法,然后我想删除键标记和直接跟在键后面的 dict 标记。

public void removeTrack (String track, File file) {
    try {
        docBuilder = docFactory.newDocumentBuilder();
    }
    catch (ParserConfigurationException pce) {
        System.out.println(pce);
    }
    try {
        XPathExpression expr;
        Document doc = docBuilder.parse(file);
        doc.getDocumentElement().normalize();
        expr = xPath.compile("/plist//key");
        NodeList keys = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

        for (int key=0; key<keys.getLength(); key++) {
            expr = xPath.compile("/plist/dict/dict/key[contains(text(),'"+track+"']");
            Node keyNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            expr = xPath.compile("/plist/dict/dict/key[contains(text(),'"+track+"')]/following-sibling::*[1]");
            Node trackDictNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            keyNode.getParentNode().removeChild(keyNode);
            trackDictNode.getParentNode().removeChild(trackDictNode);
            System.out.println(keyNode.getNodeName()+" : "+keyNode.getNodeValue());
            System.out.println(trackDictNode.getNodeName()+" : "+trackDictNode.getFirstChild());
        }
    }
    catch (SAXException e) {
        System.out.println(e);
    }
    catch (IOException e) {
        System.out.println(e);
    }
    catch (XPathExpressionException e) {
        System.out.println(e);
    }
}

我将 System.out 调用放在 for 循环的底部以查看发生了什么,然后我得到了这个:

key : null
dict : [#text: 
            ]

我认为问题在于我没有使用 doc.getDocumentElement() 或其他方式将节点连接到文件,但我不确定如何将 XPath 搜索应用于此?

【问题讨论】:

    标签: java xml dom xpath


    【解决方案1】:

    尝试以下方法:

    public void removeTrack(String track, File file) {
        try {
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(file);
            doc.getDocumentElement().normalize();
            XPath xPath = XPathFactory.newInstance().newXPath();
            XPathExpression expr = xPath.compile("/plist/dict/dict/key");
    
            expr = xPath.compile("/plist/dict/dict/key[text() = '" + track + "']");
            Node keyNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
            expr = xPath.compile("/plist/dict/dict/key[text() = '" + track + "']/following-sibling::*[1]/key");
            Node trackDictNode = (Node) expr.evaluate(doc, XPathConstants.NODE);
    
            keyNode.getParentNode().removeChild(keyNode);
            trackDictNode.getParentNode().removeChild(trackDictNode);
            System.out.println(keyNode.getNodeName() + " : " + keyNode.getTextContent());
            System.out.println(trackDictNode.getFirstChild().getNodeName() + " : "
                    + trackDictNode.getFirstChild().getNodeValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    您不需要遍历所有键,您的方法签名只允许删除一个轨道。 /plist//key 给你的结果太多,因为它也匹配第二个 dict 节点下面的 key 节点。我希望这会有所帮助。

    顺便说一句,我更改了您的代码,所以它对我有用。如果您下次可以发布独立的工作代码,那就太好了。

    【讨论】:

    • +1 用于删除 for 循环内的无用代码。在我将其更改为一次只接受一个值之前,我曾经传递一个字符串数组。
    【解决方案2】:

    您的代码看起来没问题,应该删除您想要的元素。 (希望您知道必须将 xml 文档保存回去才能看到更改)

    你误解了你的输出:

    getNodeValue 的元素为空,这就是您在标签键的输出中看到的内容。如果您使用getTextContent(),您会看到 203

    现在,对于 dict 标记,您获取它的第一个子节点,这将是一个文本节点,其中包含从 &lt;dict&gt; 末尾到下面的 &lt;key&gt; 行的空格,这就是您在输出中看到的内容.

    “预期”输出(我只是在这里猜测这是您想要的),将给出代码:

    Element trackDictNode = (Element) expr.evaluate(doc, XPathConstants.NODE);
    ....
    System.out.println(keyNode.getNodeName()+" : "+keyNode.getTextContent());
    System.out.println(trackDictNode.getNodeName()+" : "+trackDictNode.getElementsByTagName("*").item(0).getTextContent();
    

    应该打印:

    key : 203
    dict : Track Id.
    

    我可能还会使用 keyNode 的 nextSibling() 而不是另一个 XPath 表达式来获取字典元素:

    Element trackDictNode = null;
    {
       Node temp = keyNode.nextSibling();
       while (temp != null && !(temp instanceof Element)) temp = temp.nextSibling();
       trackDictNode = temp;
    
    }
    

    【讨论】:

    • trackDictNodeorg.w3c.dom.Node 类型,它没有方法 getElementsByTagName(String tagName)
    • @Zielu 为什么Node trackDictNode = keyNode.getNextSibling(); 不起作用?另外谢谢,我不敢相信我错过了这种方法底部的文件重写!尴尬!哈哈
    • keyNode 的 nextSibling() 可以是带有空格的 Text 节点,也可以是不需要 nextTag 的 cmets 节点(与 dict 的子节点相同)。但这是基于从 Dom 模型中记住的内容,并不是 100% 肯定有必要。只需尝试使用简单的 nextSibling 并查看它是否忽略 cmets 或 Texts。我写了一个“安全”的版本,没有测试行为,但我认为这是必要的
    猜你喜欢
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多