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

ken*_*dds 39 java xml parsing dom xml-parsing

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

我正在使用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>
Run Code Online (Sandbox Code Playgroud)

如您所见,您可以在两个位置放置<file>元素.无论是团体还是外部团体.我真的希望它以这种方式构建,因为它更加用户友好.

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

我想到了两个解决方案:

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

这些解决方案都不是那么令人满意,只是按照它们的方式离开事物,只获得<file>元素的直接<notification>元素.

我对IMPO的评论和答案是开放的,关于"最好"的方法,但我真的对DOM解决方案感兴趣,因为这是该项目的其余部分正在使用的.谢谢.

Biz*_*uge 21

我意识到你在5月@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>
Run Code Online (Sandbox Code Playgroud)

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

看了一会儿的API,我注意到实际上有两种方法可能有用: -

总之,这两种方法似乎提供了获取Node的所有直接后代元素所需的一切.以下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");
}

%>
Run Code Online (Sandbox Code Playgroud)

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

NODE num:-1
NODE num:-1.1
NODE num:-1.2
Run Code Online (Sandbox Code Playgroud)

希望这对任何人都有帮助.欢呼为最初的职位.

  • +1是一个非常简单,简单和干净的解决方案.你可以使用这种技术的`for`循环来保持它的优雅并保留范围:`for(Node n = docEl.getFirstChild(); n!= null; n = n.getNextSibling())`. (3认同)

Ale*_*lex 13

您可以使用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"));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它应该输出:

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"
Run Code Online (Sandbox Code Playgroud)


ken*_*dds 12

好吧,这个问题的DOM解决方案实际上非常简单,即使它不是太优雅当我filesNodeList在我调用时迭代返回时,notificationElement.getElementsByTagName("file");我只是检查父节点的名称是否是"通知".如果不是,那么我忽略它,因为它将由<group>元素处理.这是我的代码解决方案:

for (int j = 0; j < filesNodeList.getLength(); j++) {
  Element fileElement = (Element) filesNodeList.item(j);
  if (!fileElement.getParentNode().getNodeName().equals("notification")) {
    continue;
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)


art*_*hur 5

如果你坚持使用 DOM API

NodeList nodeList = doc.getElementsByTagName("notification")
    .item(0).getChildNodes();

// get the immediate child (1st generation)
for (int i = 0; i < nodeList.getLength(); i++)
    switch (nodeList.item(i).getNodeType()) {
        case Node.ELEMENT_NODE:

            Element element = (Element) nodeList.item(i);
            System.out.println("element name: " + element.getNodeName());
            // check the element name
            if (element.getNodeName().equalsIgnoreCase("file"))
            {

                // do something with you "file" element (child first generation)

                System.out.println("element name: "
                    + element.getNodeName() + " attribute: "
                    + element.getAttribute("location"));

            }
    break;

}
Run Code Online (Sandbox Code Playgroud)

我们的第一个任务是获取元素“通知”(在本例中为第一个 -item (0)-)及其所有子元素:

NodeList nodeList = doc.getElementsByTagName("notification")
    .item(0).getChildNodes();
Run Code Online (Sandbox Code Playgroud)

(稍后您可以使用获取所有元素来处理所有元素)。

对于“通知”的每个子项:

for (int i = 0; i < nodeList.getLength(); i++)
Run Code Online (Sandbox Code Playgroud)

您首先获取它的类型以查看它是否是一个元素:

switch (nodeList.item(i).getNodeType()) {
    case Node.ELEMENT_NODE:
        //.......
        break;  
}
Run Code Online (Sandbox Code Playgroud)

如果是这样,那么你得到了你的孩子“文件”,那不是孙子“通知”

您可以查看它们:

if (element.getNodeName().equalsIgnoreCase("file"))
{

    // do something with you "file" element (child first generation)

    System.out.println("element name:"
        + element.getNodeName() + " attribute: "
        + element.getAttribute("location"));

}
Run Code Online (Sandbox Code Playgroud)

并且输出是:

element name: file
element name:file attribute: C:\valid\file.txt
element name: file
element name:file attribute: C:\valid\file.xml
element name: file
element name:file attribute: C:\valid\file.doc
Run Code Online (Sandbox Code Playgroud)