[Java] XML를 다루는 방법(XMLStreamWriter, XMLStreamReader, XmlFactory 사용법)


Development note/Java  2020. 6. 15. 16:02

안녕하세요. 명월입니다.


이 글은 Java에서 XML를 다루는 방법(XMLStreamWriter, XMLStreamReader, XmlFactory 사용법)에 대한 글입니다.


프로그램를 만들때, 환경 설정 파일을 다루는 경우가 많이 있습니다. 환경 설정 파일의 구조로는 json 구조라던가 xml, ini, csv등등의 많은 파일 구조가 있습니다.

그 중 xml 형식의 파일 구조로 데이터를 취득하는 경우가 있습니다.


xml이란 인터넷을 찾아보면 eXtensible Markup Language의 약자로 확장성이 있는 마크업 언어라고 정의 되어있습니다.

이 확장성 마크업이라는 뜻은 동적으로 정의 가능한 데이터 포멧이라는 뜻입니다.

ini의 경우는 세션과 key가 있는데 key=value의 형식으로 데이터를 취득하는 방법입니다. 그 이상의 데이터를 설정하기가 쉽지 않습니다.

csv의 경우는 콤마나 탭의 구분으로 테이블 형식으로 데이터를 취득하는 방법으로 컬럼의 갯수만큼 레코드의 데이터가 정해지고 1행과 2행의 데이터의 구조는 같아 질 수 밖에 없습니다.

json은 ini와 csv보다는 조금 확장성이 용이하지만 json도 역시는 기본 key=value의 한계가 있습니다.

xml의 경우는 태그의 열고 닫음으로 데이터의 값을 표현할 수 있고 각 테그에 속성(attribute)를 동적으로 설정하므로써 데이터를 정말 자유스럽게 설정할 수 있는 장접이 있습니다.


xml를 취득하는 방법에는 여러가지가 있지만 여기서는 그중 XMLStream를 이용해서 간단하게 xml를 생성, 취득하는 방법을 소개하겠습니다.

import java.io.FileWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
public class Example {
  // 실행 함수
  public static void main(String[] args) {
    // xml factory 취득
    XMLOutputFactory factory = XMLOutputFactory.newFactory();
    // 파일 stream 취득 (자동 close)
    try (FileWriter writer = new FileWriter("d:\\work\\test.xml")) {
      // xmlwriter 생성
      XMLStreamWriter xmlwriter = factory.createXMLStreamWriter(writer);
      // <?xml version="1.0" ?> 작성
      xmlwriter.writeStartDocument();
      // <document> 작성
      xmlwriter.writeStartElement("document");
      // <data> 작성
      xmlwriter.writeStartElement("data");
      // <data name="value"> 작성
      xmlwriter.writeAttribute("name", "value");
      // <data>Hello world</data> 작성
      xmlwriter.writeCharacters("Hello world");
      // </data> 작성
      xmlwriter.writeEndElement();
      // </document> 작성
      xmlwriter.writeEndElement();
      // xml 작성 종료
      xmlwriter.writeEndDocument();
      // 갱신 (파일에 쓰기)
      xmlwriter.flush();
      // xmlwriter 닫기
      xmlwriter.close();
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
}

XMLOutputFactory의 경우는 java의 표준 클래스이기 때문에 따로 오픈 소스 라이브러리를 참조할 필요가 없습니다.


xml은 함수의 호출 순서대로 생성되며 태그의 열고 닫힘과 속성과 값이 설정됩니다.

참조 - http://docs.oracle.com/javase/7/docs/api/javax/xml/stream/XMLStreamWriter.html


이번에는 xml를 읽는 방법에 대한 소스입니다.

import java.io.FileReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
public class Example {
  // 실행 함수
  public static void main(String[] args) {
    // xml factory 취득
    XMLInputFactory factory = XMLInputFactory.newInstance();
    // 파일 stream 취득 (자동 close)
    try (FileReader reader = new FileReader("d:\\work\\test.xml")) {
      // xmlreader 생성
      XMLStreamReader xmlreader = factory.createXMLStreamReader(reader);
      // iterator 패턴으로 태그를 반복적으로 취득
      while (xmlreader.hasNext()) {
        // text에서의 태그 행 번호와 열 번호
        System.out.print("EVENT:[" + xmlreader.getLocation().getLineNumber() + "][" + xmlreader.getLocation().getColumnNumber() + "]");
        // 콘솔 출력 시작
        System.out.print(" [");
        // 태그의 타입 별로 출력 방법을 구분한다.
        switch (xmlreader.getEventType()) {
        // xml 시작 태그
        case XMLStreamReader.START_DOCUMENT: {
          // 태그 열기 출력
          System.out.print("<?xml");
          // 버젼 출력
          System.out.print(" version='" + xmlreader.getVersion() + "'");
          // 인코딩 타입 출력
          System.out.print(" encoding='" + xmlreader.getCharacterEncodingScheme() + "'");
          // standalone 타입 출력
          if (xmlreader.isStandalone()) {
            // 콘솔 출력
            System.out.print(" standalone='yes'");
          } else {
            // 콘솔 출력
            System.out.print(" standalone='no'");
          }
          // 태그 닫기 출력
          System.out.print("?>");
        }
          break;
        // 일반 시작 태그
        case XMLStreamReader.START_ELEMENT: {
          // 일반 시작 태그 출력
          System.out.print("<");
          // 태그 이름을 가지고 있을 경우
          if (xmlreader.hasName()) {
            // 접두어 취득
            String prefix = xmlreader.getPrefix();
            // uri 취득
            String uri = xmlreader.getNamespaceURI();
            // 태그 이름 취득
            String localName = xmlreader.getLocalName();
            // uri가 있으면
            if (uri != null && !"".equals(uri)) {
              // 콘솔 출력
              System.out.print("[" + uri + "]");
            }
            // 접두어가 있으면
            if (prefix != null && !"".equals(prefix)) {
              // 콘솔 출력
              System.out.print(prefix + ":");
            }
            // 태그 이름이 있으면
            if (localName != null) {
              // 콘솔 출력
              System.out.print(localName);
            }
          }
          // 네임 스페이스 갯수만큼 반복
          for (int i = 0; i < xmlreader.getNamespaceCount(); i++) {
            // 네임 스페이스 접두어 취득
            String prefix = xmlreader.getNamespacePrefix(i);
            // uri 취득
            String uri = xmlreader.getNamespaceURI(i);
            // 콘솔 출력
            System.out.print(" ");
            // 접두어가 없으면
            if (prefix == null) {
              // uri만 출력
              System.out.print("xmlns='" + uri + "'");
            } else {
              // 출력
              System.out.print("xmlns:" + prefix + "='" + uri + "'");
            }
          }
          // 태그의 속성 갯수만큼 반복
          for (int i = 0; i < xmlreader.getAttributeCount(); i++) {
            // 접두어 취득
            String prefix = xmlreader.getAttributePrefix(i);
            // 네임 스페이스 취득
            String namespace = xmlreader.getAttributeNamespace(i);
            // 속성 이름 취득
            String localName = xmlreader.getAttributeLocalName(i);
            // 값 취득
            String value = xmlreader.getAttributeValue(i);
            // 콘솔 출력
            System.out.print(" ");
            // 네임 스페이스가 있으면
            if (namespace != null && !"".equals(namespace)) {
              // 콘솔 출력
              System.out.print("[" + namespace + "]");
            }
            // 접두어가 있으면
            if (prefix != null && !"".equals(prefix)) {
              // 콘솔 출력
              System.out.print(prefix + ":");
            }
            // 속성 이름이 있으면
            if (localName != null) {
              // 콘솔 출력
              System.out.print(localName);
            }
            // 값 출력
            System.out.print("='" + value + "'");
          }
          // 일반 시작 태그 출력
          System.out.print(">");
        }
          break;
        // 일반 닫기 태그
        case XMLStreamReader.END_ELEMENT: {
          // 일반 닫기 태그 출력
          System.out.print("</");
          // 태그 이름을 가지고 있을 경우
          if (xmlreader.hasName()) {
            // 접두어 취득
            String prefix = xmlreader.getPrefix();
            // uri 취득
            String uri = xmlreader.getNamespaceURI();
            // 태그 이름 취득
            String localName = xmlreader.getLocalName();
            // url가 있으면
            if (uri != null && !"".equals(uri)) {
              // 콘솔 출력
              System.out.print("[" + uri + "]");
            }
            // 접두어가 있으면
            if (prefix != null && !"".equals(prefix)) {
              // 콘솔 출력
              System.out.print(prefix + ":");
            }
            // 태그 이름이 있으면
            if (localName != null) {
              // 콘솔 출력
              System.out.print(localName);
            }
          }
          // 일반 닫기 태그 출력
          System.out.print(">");
        }
          break;
        // 태그 공백일 경우, 문자일 경우
        case XMLStreamReader.SPACE:
        case XMLStreamReader.CHARACTERS: {
          // 문자의 시작 index
          int start = xmlreader.getTextStart();
          // 문자의 갯수
          int length = xmlreader.getTextLength();
          // 콘솔 출력
          System.out.print(new String(xmlreader.getTextCharacters(), start, length));
        }
          break;
        // 명령 태그
        case XMLStreamReader.PROCESSING_INSTRUCTION: {
          // 명령 태그 시작 출력
          System.out.print("<?");
          // 문자가 존재하면
          if (xmlreader.hasText()) {
            // 콘솔 출력
            System.out.print(xmlreader.getText());
          }
          // 명령 태그 닫기 출력
          System.out.print("?>");
        }
          break;
        // CDATA 태그 (문자 데이터)
        case XMLStreamReader.CDATA: {
          // CDATA 열기 출력
          System.out.print("<![CDATA[");
          // 문자의 시작 index
          int start = xmlreader.getTextStart();
          // 문자의 갯수
          int length = xmlreader.getTextLength();
          // 콘솔 출력
          System.out.print(new String(xmlreader.getTextCharacters(), start, length));
          // CDATA 닫기 출력
          System.out.print("]]>");
        }
          break;
        // 주석 출력
        case XMLStreamReader.COMMENT: {
          // 주석 열기 출력
          System.out.print("<!--");
          // 문자가 존재하면
          if (xmlreader.hasText()) {
            // 콘솔 출력
            System.out.print(xmlreader.getText());
          }
          // 주석 닫기 출력
          System.out.print("-->");
        }
          break;
        // 엔티티 참조
        case XMLStreamReader.ENTITY_REFERENCE: {
          // 콘솔 출력
          System.out.print(xmlreader.getLocalName() + "=");
          // 문자가 존재하면
          if (xmlreader.hasText()) {
            // 콘솔 출력
            System.out.print("[" + xmlreader.getText() + "]");
          }
        }
          break;
        }
        // 콘솔 출력 닫기
        System.out.println("]");
        // iterator 패턴의 다음 데이터
        xmlreader.next();
      }
      // xmlreader 닫기
      xmlreader.close();
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
}

위 소스를 보면 xmlreader는 각 테그의 타입 별로 반복 패턴으로 표현됩니다.

참조 - http://docs.oracle.com/javase/7/docs/api/javax/xml/stream/XMLStreamReader.html


여기까지 Java에서 XML를 다루는 방법(XMLStreamWriter, XMLStreamReader, XmlFactory 사용법)에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.