Рубрики
Без рубрики

Как читать XML-файл на Java (синтаксический анализатор SAX)

Этот учебник покажет вам, как использовать встроенный в Java синтаксический анализатор SAX для чтения и анализа XML-файла.

Этот учебник покажет вам, как использовать встроенный в Java синтаксический анализатор SAX для чтения и анализа XML-файла.

  • 1. Что такое простой API для XML (SAX)
  • 2. Читать или анализировать XML-файл (SAX)
  • 3. Преобразование XML-файла в объект
  • 4. Обработчик ошибок SAX
  • 5. Саксофон и Юникод
  • 6. Скачать Исходный Код
  • 7. Рекомендации

1. Что такое простой API для XML (SAX)

1.1 Простой API для XML (SAX) – это push-API и шаблон наблюдателя , управляемый событиями, последовательный доступ к элементам XML-файла последовательно. Этот синтаксический анализатор SAX считывает XML-файл от начала до конца, вызывает один метод при обнаружении одного элемента или вызывает другой метод при обнаружении определенного текста или атрибута.

SAX быстр и эффективен, требует гораздо меньше памяти, чем DOM, потому что SAX не создает внутреннее представление (древовидную структуру) XML-данных, как это делает DOM.

Примечание Синтаксический анализатор SAX работает быстрее и использует меньше памяти, чем анализатор DOM. SAX подходит для последовательного чтения XML-элементов; DOM подходит для манипуляций с XML, таких как создание, изменение или удаление XML-элементов.

1.2 Некоторые распространенные события САКСОФОНА:

  • Начальный документ() и Конечный документ() – Метод, вызываемый в начале и в конце XML-документа.
  • Начальный элемент() и Конечный элемент() – Метод, вызываемый в начале и в конце XML-элемента.
  • символы() – Метод, вызываемый с текстовым содержимым между началом и концом XML-элемента.

1.3 Ниже приведен простой XML-файл.

    mkyong

Анализатор SAX считывает приведенный выше XML-файл и последовательно вызывает следующие события или методы:

  1. Начальный документ()
  2. Элемент запуска()<имя>
  3. символы()mkyong
  4. Конечный элемент()
  5. конечный документ()

2. Читать или анализировать XML-файл (SAX)

В этом примере показано, как использовать встроенные в Java API-интерфейсы SAX-анализатора для чтения или анализа XML-файла.

2.1 Ниже приведен XML-файл.



    
        mkyong
        support
        5000
        
        
    
    
        yflow
        admin
        8000
        
    

P.S В XML-файле для таких специальных символов, как < или & , нам нужно обернуть его CDATA-ДАННЫЕ .

2.2 Создайте класс для расширения org.xml.sax.помощники. DefaultHandler и переопределите методы startElement , endElement и символов для печати всех XML-элементов, атрибутов, комментариев и текстов.

package com.mkyong.xml.sax.handler;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class PrintAllHandlerSax extends DefaultHandler {

  private StringBuilder currentValue = new StringBuilder();

  @Override
  public void startDocument() {
      System.out.println("Start Document");
  }

  @Override
  public void endDocument() {
      System.out.println("End Document");
  }

  @Override
  public void startElement(
          String uri,
          String localName,
          String qName,
          Attributes attributes) {

      // reset the tag value
      currentValue.setLength(0);

      System.out.printf("Start Element : %s%n", qName);

      if (qName.equalsIgnoreCase("staff")) {
          // get tag's attribute by name
          String id = attributes.getValue("id");
          System.out.printf("Staff id : %s%n", id);
      }

      if (qName.equalsIgnoreCase("salary")) {
          // get tag's attribute by index, 0 = first attribute
          String currency = attributes.getValue(0);
          System.out.printf("Currency :%s%n", currency);
      }

  }

  @Override
  public void endElement(String uri,
                         String localName,
                         String qName) {

      System.out.printf("End Element : %s%n", qName);

      if (qName.equalsIgnoreCase("name")) {
          System.out.printf("Name : %s%n", currentValue.toString());
      }

      if (qName.equalsIgnoreCase("role")) {
          System.out.printf("Role : %s%n", currentValue.toString());
      }

      if (qName.equalsIgnoreCase("salary")) {
          System.out.printf("Salary : %s%n", currentValue.toString());
      }

      if (qName.equalsIgnoreCase("bio")) {
          System.out.printf("Bio : %s%n", currentValue.toString());
      }

  }

  // http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html#characters%28char%5B%5D,%20int,%20int%29
  // SAX parsers may return all contiguous character data in a single chunk,
  // or they may split it into several chunks
  @Override
  public void characters(char ch[], int start, int length) {

      // The characters() method can be called multiple times for a single text node.
      // Some values may missing if assign to a new string

      // avoid doing this
      // value = new String(ch, start, length);

      // better append it, works for single or multiple calls
      currentValue.append(ch, start, length);

  }

}

2.3 SAXParser для анализа XML-файла.

package com.mkyong.xml.sax;

import com.mkyong.xml.sax.handler.PrintAllHandlerSax;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;

public class ReadXmlSaxParser {

    private static final String FILENAME = "src/main/resources/staff.xml";

    public static void main(String[] args) {

        SAXParserFactory factory = SAXParserFactory.newInstance();

        try {

            SAXParser saxParser = factory.newSAXParser();

            PrintAllHandlerSax handler = new PrintAllHandlerSax();
            saxParser.parse(FILENAME, handler);

        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }

    }

}

Выход

Start Document
Start Element : Company
Start Element : staff
Staff id : 1001
Start Element : name
End Element : name
Name : mkyong
Start Element : role
End Element : role
Role : support
Start Element : salary
Currency :USD
End Element : salary
Salary : 5000
Start Element : bio
End Element : bio
Bio : HTML tag testing
End Element : staff
Start Element : staff
Staff id : 1002
Start Element : name
End Element : name
Name : yflow
Start Element : role
End Element : role
Role : admin
Start Element : salary
Currency :EUR
End Element : salary
Salary : 8000
Start Element : bio
End Element : bio
Bio : a & b
End Element : staff
End Element : Company
End Document

3. Преобразование XML-файла в объект

В этом примере анализируется XML-файл и преобразуется в Список объектов. Это работает, но не рекомендуется, попробуйте ДЖАКСБ

3.1 Просмотрите тот же XML-файл.



    
        mkyong
        support
        5000
        
        
    
    
        yflow
        admin
        8000
        
    

3.2 И мы хотим преобразовать приведенный выше XML-файл в следующий объект Персонал .

package com.mkyong.xml.sax.model;

import java.math.BigDecimal;

public class Staff {

  private Long id;
  private String name;
  private String role;
  private BigDecimal salary;
  private String Currency;
  private String bio;

  //... getters, setters...toString
}

3.3 Приведенный ниже класс выполнит преобразование XML в объект.

package com.mkyong.xml.sax.handler;

import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class MapStaffObjectHandlerSax extends DefaultHandler {

    private StringBuilder currentValue = new StringBuilder();
    List result;
    Staff currentStaff;

    public List getResult() {
        return result;
    }

    @Override
    public void startDocument() {
        result = new ArrayList<>();
    }

    @Override
    public void startElement(
            String uri,
            String localName,
            String qName,
            Attributes attributes) {

        // reset the tag value
        currentValue.setLength(0);

        // start of loop
        if (qName.equalsIgnoreCase("staff")) {

            // new staff
            currentStaff = new Staff();

            // staff id
            String id = attributes.getValue("id");
            currentStaff.setId(Long.valueOf(id));
        }

        if (qName.equalsIgnoreCase("salary")) {
            // salary currency
            String currency = attributes.getValue("currency");
            currentStaff.setCurrency(currency);
        }

    }

    public void endElement(String uri,
                           String localName,
                           String qName) {

        if (qName.equalsIgnoreCase("name")) {
            currentStaff.setName(currentValue.toString());
        }

        if (qName.equalsIgnoreCase("role")) {
            currentStaff.setRole(currentValue.toString());
        }

        if (qName.equalsIgnoreCase("salary")) {
            currentStaff.setSalary(new BigDecimal(currentValue.toString()));
        }

        if (qName.equalsIgnoreCase("bio")) {
            currentStaff.setBio(currentValue.toString());
        }

        // end of loop
        if (qName.equalsIgnoreCase("staff")) {
            result.add(currentStaff);
        }

    }

    public void characters(char ch[], int start, int length) {
        currentValue.append(ch, start, length);

    }

}

3.4 Запустите его.

package com.mkyong.xml.sax;

import com.mkyong.xml.sax.handler.MapStaffObjectHandlerSax;
import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ReadXmlSaxParser2 {

    public static void main(String[] args) {

        SAXParserFactory factory = SAXParserFactory.newInstance();

        try (InputStream is = getXMLFileAsStream()) {

            SAXParser saxParser = factory.newSAXParser();

            // parse XML and map to object, it works, but not recommend, try JAXB
            MapStaffObjectHandlerSax handler = new MapStaffObjectHandlerSax();

            saxParser.parse(is, handler);

            // print all
            List result = handler.getResult();
            result.forEach(System.out::println);

        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }

    }

    // get XML file from resources folder.
    private static InputStream getXMLFileAsStream() {
        return ReadXmlSaxParser2.class.getClassLoader().getResourceAsStream("staff.xml");
    }

}

Выход

Staff{id=1001, name='揚木金', role='support', salary=5000, Currency='USD', bio='HTML tag testing'}
Staff{id=1002, name='yflow', role='admin', salary=8000, Currency='EUR', bio='a & b'}

4. Обработчик ошибок SAX

В этом примере показано, как зарегистрировать пользовательский обработчик ошибок для синтаксического анализатора SAX.

4.1 Создайте класс и расширяйте org.xml.саксофон. Обработчик ошибок . Прочитайте код для самоописания. Он просто завернул исходное сообщение об ошибке.

package com.mkyong.xml.sax.handler;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.PrintStream;

public class CustomErrorHandlerSax implements ErrorHandler {

    private PrintStream out;

    public CustomErrorHandlerSax(PrintStream out) {
        this.out = out;
    }

    private String getParseExceptionInfo(SAXParseException spe) {
        String systemId = spe.getSystemId();

        if (systemId == null) {
            systemId = "null";
        }

        String info = "URI=" + systemId + " Line="
                + spe.getLineNumber() + ": " + spe.getMessage();

        return info;
    }

    public void warning(SAXParseException spe) throws SAXException {
        out.println("Warning: " + getParseExceptionInfo(spe));
    }

    public void error(SAXParseException spe) throws SAXException {
        String message = "Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }

    public void fatalError(SAXParseException spe) throws SAXException {
        String message = "Fatal Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }

}

4.2 Мы используем SAXParser.getxmlreader() для получения org.xml.sax. XmlReader , он предоставляет больше возможностей для настройки синтаксического анализатора SAX.

package com.mkyong.xml.sax;

import com.mkyong.xml.sax.handler.CustomErrorHandlerSax;
import com.mkyong.xml.sax.handler.MapStaffObjectHandlerSax;
import com.mkyong.xml.sax.model.Staff;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ReadXmlSaxParser3 {

  public static void main(String[] args) {

      SAXParserFactory factory = SAXParserFactory.newInstance();

      try (InputStream is = getXMLFileAsStream()) {

          SAXParser saxParser = factory.newSAXParser();

          // parse XML and map to object, it works, but not recommend, try JAXB
          MapStaffObjectHandlerSax handler = new MapStaffObjectHandlerSax();

          // try XMLReader
          //saxParser.parse(is, handler);

          // more options for configuration
          XMLReader xmlReader = saxParser.getXMLReader();

          // set our custom error handler
          xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));

          xmlReader.setContentHandler(handler);

          InputSource source = new InputSource(is);

          xmlReader.parse(source);

          // print all
          List result = handler.getResult();
          result.forEach(System.out::println);

      } catch (ParserConfigurationException | SAXException | IOException e) {
          e.printStackTrace();
      }

  }

  // get XML file from resources folder.
  private static InputStream getXMLFileAsStream() {
      return ReadXmlSaxParser2.class.getClassLoader().getResourceAsStream("staff.xml");
  }

}

4.3 Обновить staff.xml , удалите CDATA в элементе bio и поместите & , и анализатор SAX выдаст ошибку.



    
        mkyong
        support
        5000
        
        &
    

4.4 Запустите его с помощью описанного выше пользовательского обработчика ошибок.

  xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));

Выход

org.xml.sax.SAXException: Fatal Error: URI=null Line=8: The entity name must immediately follow the '&' in the entity reference.
at com.mkyong.xml.sax.handler.CustomErrorHandlerSax.fatalError(CustomErrorHandlerSax.java:41)
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:181)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1471)
//...

4.5 Запустите его без специального обработчика ошибок.

  // xmlReader.setErrorHandler(new CustomErrorHandlerSax(System.err));

Выход

[Fatal Error] :8:15: The entity name must immediately follow the '&' in the entity reference.
org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 15; The entity name must immediately follow the '&' in the entity reference.
	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1243)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
	at com.mkyong.xml.sax.ReadXmlSaxParser2.main(ReadXmlSaxParser2.java:44)  

5. Саксофон и Юникод

Для XML-файлов, содержащих символы Юникода, по умолчанию SAX может следовать кодировке XML (по умолчанию UTF-8) и правильно анализировать содержимое.

5.1 Мы можем определить кодировку в верхней части XML-файла, encoding="кодировка-код" ; например, ниже приведен XML-файл, использующий кодировку UTF-8 .



    
        揚木金
        support
        5000
        
    
    
        yflow
        admin
        8000
        
    

5.2 В качестве альтернативы мы можем определить указанную кодировку в Источнике ввода .

  XMLReader xmlReader = saxParser.getXMLReader();
  xmlReader.setContentHandler(handler);

  InputSource source = new InputSource(is);

  // set encoding
  source.setEncoding(StandardCharsets.UTF_8.toString());

  //source.setEncoding(StandardCharsets.UTF_16.toString());

  xmlReader.parse(source);

Примечание Дополнительные примеры синтаксического анализатора SAX – Oracle – Простой API для XML (SAX)

6. Скачать Исходный Код

$клон git $клон git

$компакт-диск java-xml

$cd src/основной/java/com/mkyong/xml/саксофон/

7. Рекомендации

Оригинал: “https://mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/”