Использование Java для анализа XML

Я создал скрипт PHP, который анализирует XML-файл. Это непросто использовать, и я хотел реализовать его на Java.

Внутри первого элемента присутствуют различные значения wfs:member элементы элемента I проходят через:

 foreach ($data->children("wfs", true)->member as $member) { } 

Это было легко сделать с Java:

 NodeList wfsMember = doc.getElementsByTagName("wfs:member"); for(int i = 0; i < wfsMember.getLength(); i++) { } 

Я открыл XML-файл, как этот

 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document doc = documentBuilder.parse(WeatherDatabaseUpdater.class.getResourceAsStream("wfs.xml")); 

Затем мне нужно получить атрибут из элемента, называемого observerdProperty . В PHP это просто:

 $member-> children("omso", true)->PointTimeSeriesObservation-> children("om", true)->observedProperty-> attributes("xlink", true)->href как $member-> children("omso", true)->PointTimeSeriesObservation-> children("om", true)->observedProperty-> attributes("xlink", true)->href 

Но на Java, как мне это сделать? Нужно ли использовать getElementsByTagName и прокручивать их, если я хочу глубже в структуре? `

В PHP весь скрипт выглядит следующим образом.

 foreach ($data->children("wfs", true)->member as $member) { $dataType = $dataTypes[(string) $member-> children("omso", true)->PointTimeSeriesObservation-> children("om", true)->observedProperty-> attributes("xlink", true)->href]; foreach ($member-> children("omso", true)->PointTimeSeriesObservation-> children("om", true)->result-> children("wml2", true)->MeasurementTimeseries-> children("wml2", true)->point as $point) { $time = $point->children("wml2", true)->MeasurementTVP->children("wml2", true)->time; $value = $point->children("wml2", true)->MeasurementTVP->children("wml2", true)->value; $data[$dataType][] = array($time, $value) } } 

Во втором foreach я прохожу через элементы наблюдения и получаю от него данные времени и значения. Затем я сохраняю его в массиве. Если мне нужно пропустить элементы в Java, как я описал, это очень сложно реализовать. Я не думаю, что это так, так может кто-нибудь посоветует мне, как реализовать что-то подобное на Java?

Solutions Collecting From Web of "Использование Java для анализа XML"

Самый простой способ, если производительность не является главной проблемой, вероятно, XPath. С XPath вы можете найти узлы и атрибуты, просто указав путь.

 XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); XPathExpression expr = xpath.compile(<xpath_expression>); NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); 

Выражение xpath может быть таким же простым, как

 "string(//member/observedProperty/@href)" 

Для получения дополнительной информации о XPath, учебник XPath из W3Schools довольно хорош.

У вас мало вариантов реализации XML-синтаксического анализа на Java.

Наиболее распространенными являются: DOM, SAX, StAX .

У каждого есть свои плюсы и минусы. С Dom и Sax вы можете проверить свой xml с помощью схемы xsd. Но Stax работает без проверки xsd и намного быстрее.

Например, файл xml :

 <?xml version="1.0" encoding="UTF-8"?> <staff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="oldEmployee.xsd"> <employee> <name>Carl Cracker</name> <salary>75000</salary> <hiredate year="1987" month="12" day="15" /> </employee> <employee> <name>Harry Hacker</name> <salary>50000</salary> <hiredate year="1989" month="10" day="1" /> </employee> <employee> <name>Tony Tester</name> <salary>40000</salary> <hiredate year="1990" month="3" day="15" /> </employee> </staff> 

Самый длинный в реализации (на мой взгляд) DOM- парсер:

 class DomXmlParser { private Document document; List<Employee> empList = new ArrayList<>(); public SchemaFactory schemaFactory; public final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; public final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; public DomXmlParser() { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(new File(EMPLOYEE_XML.getFilename())); } catch (Exception e) { e.printStackTrace(); } } public List<Employee> parseFromXmlToEmployee() { NodeList nodeList = document.getDocumentElement().getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { Employee emp = new Employee(); NodeList childNodes = node.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { Node cNode = childNodes.item(j); // identify the child tag of employees if (cNode instanceof Element) { switch (cNode.getNodeName()) { case "name": emp.setName(text(cNode)); break; case "salary": emp.setSalary(Double.parseDouble(text(cNode))); break; case "hiredate": int yearAttr = Integer.parseInt(cNode.getAttributes().getNamedItem("year").getNodeValue()); int monthAttr = Integer.parseInt(cNode.getAttributes().getNamedItem("month").getNodeValue()); int dayAttr = Integer.parseInt(cNode.getAttributes().getNamedItem("day").getNodeValue()); emp.setHireDay(yearAttr, monthAttr - 1, dayAttr); break; } } } empList.add(emp); } } return empList; } private String text(Node cNode) { return cNode.getTextContent().trim(); } } 

SAX-синтаксический анализатор:

 class SaxHandler extends DefaultHandler { private Stack<String> elementStack = new Stack<>(); private Stack<Object> objectStack = new Stack<>(); public List<Employee> employees = new ArrayList<>(); Employee employee = null; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { this.elementStack.push(qName); if ("employee".equals(qName)) { employee = new Employee(); this.objectStack.push(employee); this.employees.add(employee); } if("hiredate".equals(qName)) { int yearatt = Integer.parseInt(attributes.getValue("year")); int monthatt = Integer.parseInt(attributes.getValue("month")); int dayatt = Integer.parseInt(attributes.getValue("day")); if (employee != null) { employee.setHireDay(yearatt, monthatt - 1, dayatt) ; } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { this.elementStack.pop(); if ("employee".equals(qName)) { Object objects = this.objectStack.pop(); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { String value = new String(ch, start, length).trim(); if (value.length() == 0) return; // skip white space if ("name".equals(currentElement())) { employee = (Employee) this.objectStack.peek(); employee.setName(value); } else if ("salary".equals(currentElement()) && "employee".equals(currentParrentElement())) { employee.setSalary(Double.parseDouble(value)); } } private String currentElement() { return this.elementStack.peek(); } private String currentParrentElement() { if (this.elementStack.size() < 2) return null; return this.elementStack.get(this.elementStack.size() - 2); } } 

Анализатор Stax:

 class StaxXmlParser { private List<Employee> employeeList; private Employee currentEmployee; private String tagContent; private String attrContent; private XMLStreamReader reader; public StaxXmlParser(String filename) { employeeList = null; currentEmployee = null; tagContent = null; try { XMLInputFactory factory = XMLInputFactory.newFactory(); reader = factory.createXMLStreamReader(new FileInputStream(new File(filename))); parseEmployee(); } catch (Exception e) { e.printStackTrace(); } } public List<Employee> parseEmployee() throws XMLStreamException { while (reader.hasNext()) { int event = reader.next(); switch (event) { case XMLStreamConstants.START_ELEMENT: if ("employee".equals(reader.getLocalName())) { currentEmployee = new Employee(); } if ("staff".equals(reader.getLocalName())) { employeeList = new ArrayList<>(); } if ("hiredate".equals(reader.getLocalName())) { int yearAttr = Integer.parseInt(reader.getAttributeValue(null, "year")); int monthAttr = Integer.parseInt(reader.getAttributeValue(null, "month")); int dayAttr = Integer.parseInt(reader.getAttributeValue(null, "day")); currentEmployee.setHireDay(yearAttr, monthAttr - 1, dayAttr); } break; case XMLStreamConstants.CHARACTERS: tagContent = reader.getText().trim(); break; case XMLStreamConstants.ATTRIBUTE: int count = reader.getAttributeCount(); for (int i = 0; i < count; i++) { System.out.printf("count is: %d%n", count); } break; case XMLStreamConstants.END_ELEMENT: switch (reader.getLocalName()) { case "employee": employeeList.add(currentEmployee); break; case "name": currentEmployee.setName(tagContent); break; case "salary": currentEmployee.setSalary(Double.parseDouble(tagContent)); break; } } } return employeeList; } } 

И некоторый основной () тест:

  public static void main(String[] args) { long startTime, elapsedTime; Main main = new Main(); startTime = System.currentTimeMillis(); main.testSaxParser(); // test elapsedTime = System.currentTimeMillis() - startTime; System.out.println(String.format("Parsing time is: %d ms%n", elapsedTime / 1000)); startTime = System.currentTimeMillis(); main.testStaxParser(); // test elapsedTime = System.currentTimeMillis() - startTime; System.out.println(String.format("Parsing time is: %d ms%n", elapsedTime / 1000)); startTime = System.currentTimeMillis(); main.testDomParser(); // test elapsedTime = System.currentTimeMillis() - startTime; System.out.println(String.format("Parsing time is: %d ms%n", elapsedTime / 1000)); } 

Вывод:

 Using SAX Parser: ----------------- Employee { name=Carl Cracker, salary=75000.0, hireDay=Tue Dec 15 00:00:00 EET 1987 } Employee { name=Harry Hacker, salary=50000.0, hireDay=Sun Oct 01 00:00:00 EET 1989 } Employee { name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 EET 1990 } Parsing time is: 106 ms Using StAX Parser: ------------------ Employee { name=Carl Cracker, salary=75000.0, hireDay=Tue Dec 15 00:00:00 EET 1987 } Employee { name=Harry Hacker, salary=50000.0, hireDay=Sun Oct 01 00:00:00 EET 1989 } Employee { name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 EET 1990 } Parsing time is: 5 ms Using DOM Parser: ----------------- Employee { name=Carl Cracker, salary=75000.0, hireDay=Tue Dec 15 00:00:00 EET 1987 } Employee { name=Harry Hacker, salary=50000.0, hireDay=Sun Oct 01 00:00:00 EET 1989 } Employee { name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 EET 1990 } Parsing time is: 13 ms 

Вы можете увидеть некоторые взгляды на варианты.

Но в java существуют другие как JAXB. Вам нужно иметь схему xsd и соглашаться с этой схемой, с которой вы генерируете классы. После этого вы можете использовать unmarchal() для чтения из xml файла:

 public class JaxbDemo { public static void main(String[] args) { try { long startTime = System.currentTimeMillis(); // create jaxb and instantiate marshaller JAXBContext context = JAXBContext.newInstance(Staff.class.getPackage().getName()); FileInputStream in = new FileInputStream(new File(Files.EMPLOYEE_XML.getFilename())); System.out.println("Output from employee XML file"); Unmarshaller um = context.createUnmarshaller(); Staff staff = (Staff) um.unmarshal(in); // print employee list for (Staff.Employee emp : staff.getEmployee()) { System.out.println(emp); } long elapsedTime = System.currentTimeMillis() - startTime; System.out.println(String.format("Parsing time is: %d ms%n", elapsedTime)); } catch (Exception e) { e.printStackTrace(); } } } 

Я пробовал этот подход по-прежнему, результат следующий:

 Employee { name='Carl Cracker', salary=75000, hiredate=1987-12-15 } } Employee { name='Harry Hacker', salary=50000, hiredate=1989-10-1 } } Employee { name='Tony Tester', salary=40000, hiredate=1990-3-15 } } Parsing time is: 320 ms 

Я добавил еще один toString() , и у него есть другой формат дня приема.

Вот несколько интересных ссылок, которые вам интересны :

  • Учебное пособие по Java и XML
  • Учебное пособие по JAXB

DOM Parser через рекурсию

Используя парсер DOM , вы можете легко войти в беспорядок вложенных циклов, как вы уже указывали. Тем не менее структура DOM представлена Node содержащим дочерние узлы в виде NodeList где каждый элемент снова является Node – это становится идеальным кандидатом для рекурсии .

Пример XML

Чтобы продемонстрировать способность DOM анализатора дисконтировать размер XML, я взял пример размещенного образца OpenWeatherMap XML.

Поиск по названию города в формате XML

Этот XML содержит прогноз погоды в Лондоне на каждые 3 часа. Этот XML делает хороший пример для чтения через относительно большой набор данных и извлечения определенной информации через атрибуты в дочерних элементах.

введите описание изображения здесь

В снимке мы нацеливаемся на сбор Elements отмеченных стрелками.

Код

Начнем с создания пользовательского класса для сохранения значений температуры и облаков . Мы бы также переопределили toString() этого настраиваемого класса, чтобы удобно печатать наши записи.

ForeCast.java

 public class ForeCast { /** * Overridden toString() to conveniently print the results */ @Override public String toString() { return "The minimum temperature is: " + getTemperature() + " and the weather overall: " + getClouds(); } public String getTemperature() { return temperature; } public void setTemperature(String temperature) { this.temperature = temperature; } public String getClouds() { return clouds; } public void setClouds(String clouds) { this.clouds = clouds; } private String temperature; private String clouds; } 

Теперь к основному классу. В основном классе, где мы выполняем нашу рекурсию, мы хотим создать List объектов ForeCast которые хранят отдельные записи температуры и облаков , пройдя весь XML.

 // List collection which is would hold all the data parsed through the XML // in the format defined by the custom type 'ForeCast' private static List<ForeCast> forecastList = new ArrayList<>(); 

В XML родительский элемент как для температуры, так и для облаков – это время , мы будем логически проверять элемент времени.

 /** * Logical block */ // As per the XML syntax our 2 fields temperature and clouds come // directly under the Node/Element time if (node.getNodeName().equals("time") && node.getNodeType() == Node.ELEMENT_NODE) { // Instantiate our custom forecast object forecastObj = new ForeCast(); Element timeElement = (Element) node; 

После этого мы получили бы информацию о параметрах температуры и облаков, которые могут быть установлены для объекта ForeCast .

  // Get the temperature element by its tag name within the XML (0th // index known) Element tempElement = (Element) timeElement.getElementsByTagName("temperature").item(0); // Minimum temperature value is selectively picked (for proof of concept) forecastObj.setTemperature(tempElement.getAttribute("min")); // Similarly get the clouds element Element cloudElement = (Element) timeElement.getElementsByTagName("clouds").item(0); forecastObj.setClouds(cloudElement.getAttribute("value")); 

Полный класс ниже:

CustomDomXmlParser.java

 import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class CustomDomXmlParser { // List collection which is would hold all the data parsed through the XML // in the format defined by the custom type 'ForeCast' private static List<ForeCast> forecastList = new ArrayList<>(); public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { // Read XML throuhg a URL (a FileInputStream can be used to pick up an // XML file from the file system) InputStream path = new URL( "http://api.openweathermap.org/data/2.5/forecast?q=London,us&mode=xml") .openStream(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(path); // Call to the recursive method with the parent node traverse(document.getDocumentElement()); // Print the List values collected within the recursive method for (ForeCast forecastObj : forecastList) System.out.println(forecastObj); } /** * * @param node */ public static void traverse(Node node) { // Get the list of Child Nodes immediate to the current node NodeList list = node.getChildNodes(); // Declare our local instance of forecast object ForeCast forecastObj = null; /** * Logical block */ // As per the XML syntax our 2 fields temperature and clouds come // directly under the Node/Element time if (node.getNodeName().equals("time") && node.getNodeType() == Node.ELEMENT_NODE) { // Instantiate our custom forecast object forecastObj = new ForeCast(); Element timeElement = (Element) node; // Get the temperature element by its tag name within the XML (0th // index known) Element tempElement = (Element) timeElement.getElementsByTagName( "temperature").item(0); // Minimum temperature value is selectively picked (for proof of // concept) forecastObj.setTemperature(tempElement.getAttribute("min")); // Similarly get the clouds element Element cloudElement = (Element) timeElement.getElementsByTagName( "clouds").item(0); forecastObj.setClouds(cloudElement.getAttribute("value")); } // Add our foreCastObj if initialized within this recursion, that is if // it traverses the time node within the XML, and not in any other case if (forecastObj != null) forecastList.add(forecastObj); /** * Recursion block */ // Iterate over the next child nodes for (int i = 0; i < list.getLength(); i++) { Node currentNode = list.item(i); // Recursively invoke the method for the current node traverse(currentNode); } } } 

Выход

Как вы можете понять из приведенного ниже скриншота, мы смогли объединить два конкретных элемента и эффективно присвоить их значения экземпляру Java Collection . Мы делегировали комплексный синтаксический анализ xml на общее рекурсивное решение и настраивали главным образом часть logical block . Как уже упоминалось, это генетическое решение с минимальной настройкой, которое может работать через все допустимые xmls .

введите описание изображения здесь

альтернативы

Доступны многие другие альтернативы, вот список парсеров XML с открытым исходным кодом для Java .

Тем не менее, ваш подход с PHP и ваша первоначальная работа с парсером на основе Java выравниваются с решением парсер XML на основе DOM, упрощенным с помощью рекурсии.

API Java, предоставляя вам все, что вам нужно, довольно нелепо использовать, как вы можете видеть. Вы можете проверить Xsylum на что-то более прямолинейное:

(Угадайте, как структурирован ваш XML):

 List<XmlElement> elements = Xsylum.elementFor(xmlFile).getAll("wfs:member"); for (XmlElement e : elements) String dataType = e.get("omso").get("om").attribute("xlink"); 

Как было предложено в другом месте, вы также можете просто использовать XPath для извлечения того, что вам нужно, что также просто с Xsylum:

 List<String> values = Xsylum.documentFor(xmlFile).values("//omso/om/@href"); 

Я бы не предложил вам реализовать свою собственную функцию синтаксического анализа для синтаксического анализа XML, поскольку там уже много опций. Мое предложение – парсер DOM. Вы можете найти несколько примеров в следующей ссылке. (Вы также можете выбрать из других доступных опций)

http://www.javacodegeeks.com/2013/05/parsing-xml-using-dom-sax-and-stax-parser-in-java.html

Вы можете использовать такие команды, как

 eElement.getAttribute("id"); 

Источник: http://www.mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/

Я согласен с тем, что уже было опубликовано о том, что вы сами не выполняете функции синтаксического анализа.

Вместо анализаторов DOM / SAX / STAX я бы предложил использовать JDOM или XOM, которые являются внешними библиотеками.

Связанные дискуссиях:

  • Какую библиотеку Java XML вы рекомендуете (чтобы заменить dom4j)?
  • Должен ли я использовать JDOM с Java 5 или 6?

Чувство моего чувства – это то, что jdom – это тот, который использует большинство разработчиков Java. Некоторые используют dom4j, некоторые xom, некоторые другие, но вряд ли кто-нибудь реализует эти функции синтаксического анализа.

использовать Java startElement и endElement для DOM Parsers