按名称仅获取 XML 直接子元素

2022-09-01 00:07:35

我的问题是:当有其他元素与父元素的“孙子”同名时,如何直接在特定的父元素下获取元素。

我正在使用Java DOM库来解析XML元素,但我遇到了麻烦。以下是我正在使用的xml的一(一小部分):

<notifications>
  <notification>
    <groups>
      <group name="zip-group.zip" zip="true">
        <file location="C:\valid\directory\" />
        <file location="C:\another\valid\file.doc" />
        <file location="C:\valid\file\here.txt" />
      </group>
    </groups>
    <file location="C:\valid\file.txt" />
    <file location="C:\valid\file.xml" />
    <file location="C:\valid\file.doc" />
  </notification>
</notifications>

如您所见,有两个位置可以放置元素。成组或组外。我真的希望它以这种方式构建,因为它更加用户友好。<file>

现在,每当我调用它时,它都会给我所有元素,包括元素下的元素。我以不同的方式处理这些类型的文件,因此不需要此功能。notificationElement.getElementsByTagName("file");<file><group>

我想到了两种解决方案:

  1. 获取文件元素的父元素并相应地处理它(取决于它是 还是 。<notification><group>
  2. 重命名第二个元素以避免混淆。<file>

这两种解决方案都不如仅仅让事物保持原样,只得到作为元素直接子元素的元素。<file><notification>

我对IMPO关于“最佳”方法的评论和答案持开放态度,但我对DOM解决方案非常感兴趣,因为这是本项目其余部分正在使用的。谢谢。


答案 1

我意识到你在五月份找到了解决这个问题的方法@kentcdodds但我刚刚遇到了一个非常相似的问题,我认为(也许在我的用例中,但不是在你的用例中)找到了解决方案。

我的XML格式的一个非常简单的例子如下所示:-

<?xml version="1.0" encoding="utf-8"?>
<rels>
    <relationship num="1">
        <relationship num="2">
            <relationship num="2.1"/>
            <relationship num="2.2"/>
        </relationship>
    </relationship>
    <relationship num="1.1"/>
    <relationship num="1.2"/>

</rels>

正如你希望从这个片段中看到的那样,我想要的格式可以为[关系]节点提供N级嵌套,所以很明显,我在Node.getChildNodes()上遇到的问题是,我从层次结构的所有级别获取所有节点,并且没有任何关于节点深度的提示。

看了一会儿API,我注意到实际上还有另外两种方法可能有一些用处:

总之,这两种方法似乎提供了获取节点的所有直接后代元素所需的一切。下面的 jsp 代码应该给出了如何实现它的相当基本的想法。对不起 JSP。我现在正在将其滚动到Bean中,但没有时间从挑选的代码中创建一个完全工作的版本。

<%@page import="javax.xml.parsers.DocumentBuilderFactory,
                javax.xml.parsers.DocumentBuilder,
                org.w3c.dom.Document,
                org.w3c.dom.NodeList,
                org.w3c.dom.Node,
                org.w3c.dom.Element,
                java.io.File" %><% 
try {

    File fXmlFile = new File(application.getRealPath("/") + "/utils/forms-testbench/dom-test/test.xml");
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    Document doc = dBuilder.parse(fXmlFile);
    doc.getDocumentElement().normalize();

    Element docEl = doc.getDocumentElement();       
    Node childNode = docEl.getFirstChild();     
    while( childNode.getNextSibling()!=null ){          
        childNode = childNode.getNextSibling();         
        if (childNode.getNodeType() == Node.ELEMENT_NODE) {         
            Element childElement = (Element) childNode;             
            out.println("NODE num:-" + childElement.getAttribute("num") + "<br/>\n" );          
        }       
    }

} catch (Exception e) {
    out.println("ERROR:- " + e.toString() + "<br/>\n");
}

%>

此代码将提供以下输出,仅显示初始根节点的直接子元素。

NODE num:-1
NODE num:-1.1
NODE num:-1.2

希望这无论如何都能帮助某人。为最初的帖子干杯。


答案 2

为此,您可以使用 XPath,使用两个路径来获取它们并以不同的方式处理它们。

要获取节点的直接子级和正在使用的节点。<file><notification>//notification/file<group>//groups/group/file

这是一个简单的示例:

public class SO10689900 {
    public static void main(String[] args) throws Exception {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader("<notifications>\n" + 
                "  <notification>\n" + 
                "    <groups>\n" + 
                "      <group name=\"zip-group.zip\" zip=\"true\">\n" + 
                "        <file location=\"C:\\valid\\directory\\\" />\n" + 
                "        <file location=\"C:\\this\\file\\doesn't\\exist.grr\" />\n" + 
                "        <file location=\"C:\\valid\\file\\here.txt\" />\n" + 
                "      </group>\n" + 
                "    </groups>\n" + 
                "    <file location=\"C:\\valid\\file.txt\" />\n" + 
                "    <file location=\"C:\\valid\\file.xml\" />\n" + 
                "    <file location=\"C:\\valid\\file.doc\" />\n" + 
                "  </notification>\n" + 
                "</notifications>")));
        XPath xpath = XPathFactory.newInstance().newXPath();
        XPathExpression expr1 = xpath.compile("//notification/file");
        NodeList nodes = (NodeList)expr1.evaluate(doc, XPathConstants.NODESET);
        System.out.println("Files in //notification");
        printFiles(nodes);

        XPathExpression expr2 = xpath.compile("//groups/group/file");
        NodeList nodes2 = (NodeList)expr2.evaluate(doc, XPathConstants.NODESET);
        System.out.println("Files in //groups/group");
        printFiles(nodes2);
    }

    public static void printFiles(NodeList nodes) {
        for (int i = 0; i < nodes.getLength(); ++i) {
            Node file = nodes.item(i);
            System.out.println(file.getAttributes().getNamedItem("location"));
        }
    }
}

它应该输出:

Files in //notification
location="C:\valid\file.txt"
location="C:\valid\file.xml"
location="C:\valid\file.doc"
Files in //groups/group
location="C:\valid\directory\"
location="C:\this\file\doesn't\exist.grr"
location="C:\valid\file\here.txt"