Чтение XML в Java с помощью DOM
14 марта, 2010 14 комментариев
Для работы с XML в Java есть достаточно большой набор инструментов, начиная от встроенных возможностей (я имею в виду возможности, которые предоставляет Core Java без использования дополнительных lib’ов) и заканчивая большим набором разнообразного стороннего кода, оформленного в виде отдельных библиотек. К примеру есть очень интересная библиотека, которую я совсем недавно использовал для того, чтобы сериализовывать класс в XML и наоборот, создавать из XML представления класса конкретный экземпляр. Библиотека называется XStream, если кому интересно, то вот она http://xstream.codehaus.org Пока что остановимся на средствах, которые входят в стандартную JDK, а именно на DOM — Document Object Model.
Так что же такое DOM? Судя из названия это есть объектная модель документа. XML документ представляет собой набор тегов — узлов. Каждый узел может иметь неограниченное количество дочерних узлов. Каждый дочерний тоже может содержать много-много потомков или не содержать их вовсе. Таким образом получается некое дерево. Так вот DOM представляет собой всё это дерево в виде специальных объектов Node. Каждый Node соответствует своему XML-тегу. Каждый Node содержит полную информацию о том, что это за тег, какие он имеет атрибуты, какие дочерние узлы содержит внутри себя и так далее. На самой вершине этой иерархии находится Document.
Для того, чтобы получить объект Document для нашего XML-файла необходимо выполнить следующий код.
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); f.setValidating(false); DocumentBuilder builder = f.newDocumentBuilder(); Document doc = builder.parse(new File("test.xml"));
У нас имеется корневой узел документа Document. Теперь при помощи DOM методов можно произвести разбор документа, добраться до нужного узла иерархии и прочитать его свойства. Можно получить список дочерних узлов при помощи метода getChildNodes.
NodeList methodNodes = node.getChildNodes();
Вот так можно пробежаться по всем дочерним узлам текущего узла:
NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); }
С помощью getAttributes получается NamedNodeMap, который содержит атрибуты узла. Вот так можно получить значение атрибута «name»:
NamedNodeMap attributes = node.getAttributes(); Node nameAttrib = attributes.getNamedItem("name"); String name = nameAttrib.getNodeValue();
Используя метод getNodeType можно узнать тип узла вот так:
if (node.getNodeType() == Node.ELEMENT_NODE) { ... }
Посмотрим теперь на тестовый пример. Тестовое приложение читает тестовый xml файл, содержимое которого представлено ниже, извлекает данные из него и отображает пользователю.
<?xml version="1.0" encoding="UTF-8"?> <application> <class name = "MainClass"> <method name = "main"/> </class> <class name = "Window"> <method name = "open"/> <method name = "close"/> <method name = "show"/> <method name = "hide"/> </class> <class name = "DataBase"> <method name = "connect"/> <method name = "disconnect"/> <method name = "getData"/> </class> </application>
А это код тестового приложения, которое должно выглядеть так, как показано на рисунке ниже.
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class TestFrame extends JFrame { private static final String SPACE = " "; /** * Создаем интерфейс приложения. */ public static void createGUI() { final JFrame frame = new JFrame("Test frame"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final Font font = new Font("Verdana", Font.PLAIN, 13); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); final JTextArea textArea = new JTextArea(15, 10); panel.add(new JScrollPane(textArea), BorderLayout.CENTER); textArea.setFont(font); JButton parseButton = new JButton("Parse XML"); parseButton.setFont(font); panel.add(parseButton, BorderLayout.SOUTH); parseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { Document doc = getDocument(); showDocument(doc, textArea); } catch (Exception ex) { JOptionPane.showMessageDialog(frame, ex.getMessage()); } } }); frame.getContentPane().add(panel); frame.setPreferredSize(new Dimension(280, 220)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } /** * Возвращает объект Document, который является объектным представлением * XML документа. */ private static Document getDocument() throws Exception { try { DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); f.setValidating(false); DocumentBuilder builder = f.newDocumentBuilder(); return builder.parse(new File("test.xml")); } catch (Exception exception) { String message = "XML parsing error!"; throw new Exception(message); } } private static void showDocument(Document doc, JTextArea textArea) { StringBuffer content = new StringBuffer(); Node node = doc.getChildNodes().item(0); ApplicationNode appNode = new ApplicationNode(node); content.append("Application \n"); List<ClassNode> classes = appNode.getClasses(); for (int i = 0; i < classes.size(); i++) { ClassNode classNode = classes.get(i); content.append(SPACE + "Class: " + classNode.getName() + " \n"); List<MethodNode> methods = classNode.getMethods(); for (int j = 0; j < methods.size(); j++) { MethodNode methodNode = methods.get(j); content.append(SPACE + SPACE + "Method: " + methodNode.getName() + " \n"); } } textArea.setText(content.toString()); } public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame.setDefaultLookAndFeelDecorated(true); createGUI(); } }); } /** * Объектное представление приложения. */ public static class ApplicationNode { Node node; public ApplicationNode(Node node) { this.node = node; } public List<ClassNode> getClasses() { ArrayList<ClassNode> classes = new ArrayList<ClassNode>(); /** * Получаем список дочерних узлов для данного узла XML, который * соответствует приложению application. Здесь будут располагаться * все узлы Node, каждый из которых является объектным * представлением тега class для текущего тега application. */ NodeList classNodes = node.getChildNodes(); for (int i = 0; i < classNodes.getLength(); i++) { Node node = classNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { /** * Создаем на основе Node узла своё объектное * представление класса. */ ClassNode classNode = new ClassNode(node); classes.add(classNode); } } return classes; } } /** * Объектное представление класса. */ public static class ClassNode { Node node; /** * Создаем новый экземпляр объекта на основе Node узла. */ public ClassNode(Node node) { this.node = node; } /** * Возвращает список методов класса. */ public List<MethodNode> getMethods() { ArrayList<MethodNode> methods = new ArrayList<MethodNode>(); /** * Получаем список дочерних узлов для данного узла XML, * который соответствует классу class. Здесь будут располагаться * все узлы Node, каждый из которых является объектным * представлением тега method для текущего тега class. */ NodeList methodNodes = node.getChildNodes(); for (int i = 0; i < methodNodes.getLength(); i++) { node = methodNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { /** * Создаем на основе Node узла своё объектное представление * метода. */ MethodNode methodNode = new MethodNode(node); methods.add(methodNode); } } return methods; } /** * Возвращае имя класса. */ public String getName() { /** * Получаем атрибуты узла метода. */ NamedNodeMap attributes = node.getAttributes(); /** * Получаем узел аттрибута. */ Node nameAttrib = attributes.getNamedItem("name"); /** * Возвращаем значение атрибута. */ return nameAttrib.getNodeValue(); } } /** * Объектное представление сущности метод класса. */ public static class MethodNode { Node node; /** * Создаем новый экземпляр объекта на основе Node узла. */ public MethodNode(Node node) { this.node = node; } /** * Возвращает имя метода. */ public String getName() { /** * Получаем атрибуты узла метода. */ NamedNodeMap attributes = node.getAttributes(); /** * Получаем узел аттрибута. */ Node nameAttrib = attributes.getNamedItem("name"); /** * Возвращаем значение атрибута. */ return nameAttrib.getNodeValue(); } } }
Небольшое добавление. Как раз разбирался именно с этим примером, но только у меня тестовый XML-файл содержит информацию не в атрибутах, а внутри элементов. Когда начал у себе переделывать класс MethodNode, то по ошибке пытался прочитать содержимое элемента с помощью
node.getNodeValue()
и получал в результате null. Сперва не понял, в чем тема — потом разобрался. Вместо это следует использовать:
node.getTextContent()
Мелочь, конечно, но может — кому пригодиться…
Спасибо за замечание. Помогло;)
Другой метод: node.getFirstChild().getNodeValue();
Под Android java почему-то нет getTextContent().
Спасибо за статью, очень нужная, одна у вас в коде есть ошибка:
скорее всего не
NodeList methodNodes = node.getChildNodes();
а
NodeList methodNodes = doc.getChildNodes();
ибо переменная node нигде не объявлена.
node — поле класса ClassNode в данном случае, у автора все верно.
Очень хорошая статья.
Было бы не плохо почитать еще и про запись данных в XML.
Здравствуйте. У меня такая проблема. Имею исходный файл
qqqq
Считываю его при помощи DOM анализатора и записываю обратно. В итоге получаю
qqqq
Соответственно вопрос. Как сделать так чтобы не преобразовывалась в ?
Вот у всех примеры с линейным файлом и с единичной глубиной вложенности… 😦
Это точно. У меня хаотичный документ с неопределенной вложенностью..
return builder.parse(newFile(«res/xml/as.xml»));
парни вот тут бьет ошибку, помоги как правильно загружать файл? или может какие-то права в манифесте прописывать надо? я никак не пойму что нужно сделать
return builder.parse(newFile(«res//xml//as.xml»));
вместо открытого файла возвращает null
виноват — показалось
Все идеально, кроме JFrame. Уровень статьи, не для повышения квалификации специалистов, а для новичка JFrame здесь на себя отвлекает внимание. Нужно было просто в консоль вывести.