Если вам необходимо найти информацию в XML-документе, то обязательно придется организовать обход древовидной структуры DOM. Язык XPath упрощает доступ к узлам дерева. Предположим, например, что в нашем распоряжении находится в XML-документ, приведенный ниже.
1 2 3 4 5 6 7 8 |
<configuration> ... <database> <username>dbuser</username> <password>secret</password> ... </database> </configuration> |
Вам необходимо определить имя пользователя, обрабатывая выражение XPath:
1 |
/configuration/database/username |
При наличии такого выражения поиск становится несколько проще, чем путем непосредственного анализа дерева DOM.
- Получение узла документа.
- Перечисление его дочерних узлов.
- Обнаружение элемента database.
- Получение первого дочернего элемента, username.
- Получение первого дочернего узла, который имеет тип Text.
- Получение данных.
Язык XPath позволяет описывать набор узлов в XML-документе. Например, следующее выражение описывает набор элементов row, которые являются дочерними по отношению к корневому элементу:
1 |
/gridbag/row |
Для выбора конкретного элемента используется операция []. Следующее выражение определяет первый дочерний элемент(отчет индексов начинается с единицы):
1 |
/gridbag/row[1] |
Для получения значений атрибутов применяется операция @. Приведенное ниже выражение XPath описывает все узлы, соответствующие атрибутам anchor элементов cell. Элементы cell должны выходить в состав элементов row, которые являются дочерними по отношению к корневому элементу gridbag.
1 |
/gridbag/row/cell/@anchor |
Существует ряд функций XPath, упрощающих работу с документом. Например, показанное ниже выражение позволяет определить число элементов row, дочерних по отношению к корневому элементу gridbag:
1 |
count(/gridbag/row) |
Примеры выражений XPath, в том числе довольно сложных, можно найти в спецификации http://www.w3c.org/TR/xpath. Существует также очень удачно составленное руководство, расположенное по адресу http://zvon.org/xxl/XPathTutorial/General/examples.html
В состав Java 5.0 включен API для обработки выражений XPath. В первую очередь надо создать объект XPath, используя для этого класс XPathFactory:
1 2 |
XPathFactory xpathfactory = XPathFactory.newInstance(); path = xpathfactory.newXPath(); |
После этого можно вызывать метод evaluate(), обрабатывающий выражения XPath:
1 |
String username = path.evaluate("/configuration/database/username", doc); |
Один объект XPath можно применять для обработки нескольких выражений. Данный вариант метода evaluate() возвращает результат в виде строки. Это удобно для получения текста, каковым является содержимое узла username в предыдущем примере. Если выражение XPath описывает набор узлов, то для его обработки можно использовать следующий вызов:
1 2 |
NodeList nodes = (NodeList) path.evaluate("/gridbag/row", doc, XPathConstants.NODESET); |
Если результатом является один узел, то в качестве третьего параметра метода evauate() нужно указать значение XPathConstants.NODE:
1 |
Node node = (Node) path.evaluate("/gridabg/row[1]", doc, XPathConstants.NODE); |
Если результатом является число, применяется константа XPathConstants.NUMBER:
1 2 |
int count = ((Number) path.evaluate("count(/gridbag/row)", doc, XPathConstants.NUMBER)).intValue(); |
Начинать поиск не обязательно с корневого узла документа. В качестве исходной точки может выступать любой узел, и даже список узлов. Например, получив узел в результате обработки приведенного выше выражения, можно вызвать метод evaluate() следующим образом:
1 |
result = path.evaluate(expression, node); |
Программа, код которой приведен ниже, иллюстрирует процесс обработки выражений XPath. Загрузите XML-файл и введите выражение. Выберите тип выражения и щелкните на кнопке Evaluate. Результат обработки выражения отобразится в нижней части окна рис 1.
Рис.1. Обработка выражений XPath
Код программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
import java.awt.*; import java.awt.event.*; import java.io.*; import javax.swing.*; import javax.swing.border.*; import javax.xml.namespace.*; import javax.xml.parsers.*; import javax.xml.xpath.*; import org.w3c.dom.*; import org.xml.sax.*; /** * Программа для обработки выражений XPath. */ public class XPathTest { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { JFrame frame = new XPathFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } } /** * В данном файле отображается XML-документ, панель для ввода * выражений XPath и поле редактирования для отображения результатов. */ class XPathFrame extends JFrame { public XPathFrame() { setTitle("XPathTest"); JMenu fileMenu = new JMenu("File"); JMenuItem openItem = new JMenuItem("Open"); openItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { openFile(); } }); fileMenu.add(openItem); JMenuItem exitItem = new JMenuItem("Exit"); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); JMenuBar menuBar = new JMenuBar(); menuBar.add(fileMenu); setJMenuBar(menuBar); ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent event) { evaluate(); } }; expression = new JTextField(20); expression.addActionListener(listener); JButton evaluateButton = new JButton("Evaluate"); evaluateButton.addActionListener(listener); typeCombo = new JComboBox(new Object[] { "STRING", "NODE", "NODESET", "NUMBER", "BOOLEAN" }); typeCombo.setSelectedItem("STRING"); JPanel panel = new JPanel(); panel.add(expression); panel.add(typeCombo); panel.add(evaluateButton); docText = new JTextArea(10, 40); result = new JTextField(); result.setBorder(new TitledBorder("Result")); add(panel, BorderLayout.NORTH); add(new JScrollPane(docText), BorderLayout.CENTER); add(result, BorderLayout.SOUTH); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); builder = factory.newDocumentBuilder(); } catch(ParserConfigurationException e) { JOptionPane.showMessageDialog(this, e); } XPathFactory xpfactory = XPathFactory.newInstance(); path = xpfactory.newXPath(); pack(); } /* * Открытие файла и загрузка документов. */ public void openFile() { JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(".")); chooser.setFileFilter(new javax.swing.filechooser.FileFilter() { public boolean accept(File f) { return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml"); } public String getDescription() { return "XML files"; } }); int r = chooser.showOpenDialog(this); if(r != JFileChooser.APPROVE_OPTION) return; File f = chooser.getSelectedFile(); try { byte[] bytes = new byte[(int) f.length()]; new FileInputStream(f).read(bytes); docText.setText(new String(bytes)); doc = builder.parse(f); } catch(IOException e) { JOptionPane.showMessageDialog(this, e); } catch(SAXException e) { JOptionPane.showMessageDialog(this, e); } } public void evaluate() { try { String typeName = (String) typeCombo.getSelectedItem(); QName returnType = (QName) XPathConstants.class.getField(typeName).get(null); Object evalResult = path.evaluate(expression.getText(), doc, returnType); if(typeName.equals("NODESET")) { NodeList list = (NodeList) evalResult; StringBuilder builder = new StringBuilder(); builder.append("{"); for(int i = 0; i < list.getLength(); i++) { if(i > 0) builder.append(", "); builder.append("" + list.item(i)); } builder.append("}"); result.setText("" + builder); } else result.setText("" + evalResult); } catch(XPathExpressionException e) { result.setText("" + e); } catch(Exception e) // Исключение, связанное с отражением { e.printStackTrace(); } } private DocumentBuilder builder; private Document doc; private XPath path; private JTextField expression; private JTextField result; private JTextArea docText; private JComboBox typeCombo; } |