Мой вопрос заключается в следующем: Как я могу получить элементы непосредственно под определенным родительским элементом, когда есть другие элементы с тем же именем, что и "внук" родительского элемента.
Я использую Java DOM библиотеку для разбора XML Elements и столкнулся с проблемой. Вот некоторая (небольшая часть) 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>
. Я работаю с каждым из этих типов файлов по-разному, поэтому такая функциональность нежелательна.
Я придумал два решения:
<уведомлением>
или <группой>
.<file>
, чтобы избежать путаницы.Ни одно из этих решений не является столь же желательным, как просто оставить все как есть и получать только те элементы <file>
, которые являются прямыми дочерними элементами <notification>
.
Я открыт для IMPO комментариев и ответов о "лучшем" способе сделать это, но я'действительно заинтересован в DOM решениях, потому что это то, что использует остальная часть этого проекта. Спасибо.
Я понимаю, вы нашли решение для этого в мае @kentcdodds, но у меня была довольно похожая проблема, которую я'вэ теперь нашли, я думаю (возможно, в моем usecase, но не в вашей), решение.
очень упрощенный пример моего формата 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-уровень вложенности [отношения] узлов, поэтому очевидно, что проблема, которую я имел с узлом.getChildNodes() было то, что я получаю все узлы на всех уровнях иерархии, и без какой-либо намек на узел глубину.
Глядя на АПИ на некоторое время , я заметил, что есть на самом деле два других метода, которые могут пригодиться:-
Вместе, эти два способа показались предложить все, что необходимо, чтобы получить все непосредственные дочерние элементы узла. Следующий код JSP должны дать довольно простые идеи, как реализовать это. Извините за ССП. Я'м прокатки в зернах, но теперь я'т иметь время, чтобы создать полностью рабочую версию взял только код.
<%@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
Надеюсь, что это кто-то все равно помогает. Ура начальном посте.
Для этого можно использовать 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"
Ну, DOM-решение этого вопроса на самом деле довольно простое, хотя и не слишком элегантное. Когда я перебираю filesNodeList
, который возвращается, когда я вызываю notificationElement.getElementsByTagName("file");
, я просто проверяю, является ли имя родительского узла "notification." Если нет, то я игнорирую его, потому что это будет обработано элементом <group>
. Вот мое кодовое решение:
for (int j = 0; j < filesNodeList.getLength(); j++) {
Element fileElement = (Element) filesNodeList.item(j);
if (!fileElement.getParentNode().getNodeName().equals("notification")) {
continue;
}
...
}
Если вы придерживаетесь API DOM
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;
}
Наша первая задача - получить элемент "Уведомление" (в данном случае первый -item (0)-) и все его дочерние элементы:
NodeList nodeList = doc.getElementsByTagName("notification")
.item(0).getChildNodes();
(позже вы сможете работать со всеми элементами, используя get all the elements).
Для каждого дочернего элемента "Уведомление":
for (int i = 0; i < nodeList.getLength(); i++)
вы сначала получаете его тип, чтобы узнать, является ли он элементом:
switch (nodeList.item(i).getNodeType()) {
case Node.ELEMENT_NODE:
//.......
break;
}
Если это так, то вы получили дочерние элементы "файл", которые не являются внучатыми детьми "Уведомления"
и вы можете проверить их:
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"));
}
и оуптут:
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
У меня была такая же проблема в одном из моих проектов, и написал небольшую функцию, которая будет возвращать список<элемент>
, содержащую только непосредственные дети.
В основном это проверка для каждого узла возвращаемых метод getelementsbytagname
если это'ы parentNode фактически узел мы ищем ребенка:
public static List<Element> getDirectChildsByTag(Element el, String sTagName) {
NodeList allChilds = el.getElementsByTagName(sTagName);
List<Element> res = new ArrayList<>();
for (int i = 0; i < allChilds.getLength(); i++) {
if (allChilds.item(i).getParentNode().equals(el))
res.add((Element) allChilds.item(i));
}
return res;
}
Принято отвечать на kentcdodds будет возвращать неверные результаты (например, внучата) если есть childnode называемые "уведомление" ПО - например, возвращение внучата, когда элемент "в группе" бы именем "уведомление" по. Я стояла перед что настройки в моем проекте, поэтому я придумал свою функцию.
Я столкнулся с проблемой, где мне нужно обрабатывать только непосредственных дочерних узлов, даже несмотря на лечение всех "файл" узлы похож. На мое решение, я сравниваю элемент'ы родительский узел с узел, который обрабатывается для того, чтобы определить, является ли элемент непосредственного ребенка.
NodeList fileNodes = parentNode.getElementsByTagName("file");
for(int i = 0; i < fileNodes.getLength(); i++){
if(parentNode.equals(fileNodes.item(i).getParentNode())){
if (fileNodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
//process the child node...
}
}
}
Я написал эту функцию, чтобы получить значение узла, имя тега, ограничение на верхний уровень
public static String getValue(Element item, String tagToGet, String parentTagName) {
NodeList n = item.getElementsByTagName(tagToGet);
Node nodeToGet = null;
for (int i = 0; i<n.getLength(); i++) {
if (n.item(i).getParentNode().getNodeName().equalsIgnoreCase(parentTagName)) {
nodeToGet = n.item(i);
}
}
return getElementValue(nodeToGet);
}
public final static String getElementValue(Node elem) {
Node child;
if (elem != null) {
if (elem.hasChildNodes()) {
for (child = elem.getFirstChild(); child != null; child = child
.getNextSibling()) {
if (child.getNodeType() == Node.TEXT_NODE) {
return child.getNodeValue();
}
}
}
}
return "";
}