메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

JAXP의 모든 것, Part 1

XML 프로세싱 툴킷을 파싱과 밸리데이션에 활용하기

Brett McLaughlin, 기자 겸 편집자, O'Reilly Media, Inc.
Brett McLaughlin은 O'Reilly Media, Inc.의 기자 겸 편집자이다.

요약:  Java API for XML Processing (JAXP)을 사용하면 다른 API를 사용하는 XML을 유효성 검사, 파싱, 변형할 수 있다. JAXP는 사용하기 쉽고 벤더 중립적이다. 이 글에서, API의 파싱과 밸리데이션 기능을 활용하는 방법을 설명한다. Part 2에서는 JAXP를 사용한 XSL 변형을 다루도록 하겠다.

원문 게재일:  2005 년 5 월 17 일
난이도:  초급
페이지뷰:  2361 회
의견:  


자바와 XML은 지난 5년 동안 가장 중요한 프로그래밍 개발이다. 결국, 자바에서 XML로 작동하는 API들이 많이 양산되었다. 가장 대중적인 Document Object Model (DOM)과 Simple API for XML (SAX)는 많은 관심들을 이끌어냈고 JDOM과 데이터 바인딩 API가 그 뒤를 따랐다.(참고자료) 한 두개 정도의 API를 이해하는 것이 임무가 되었다. 이 모든 것을 정확히 사용할 정도가 되면 ‘구루’가 된다. 하지만 이제 더 이상 자바 개발자들은 값비싼 SAX와 DOM을 알 필요가 없음을 느끼고 있다. Sun Microsystems의 JAXP 툴킷 때문이다. The Java API for XML Processing (JAXP)을 사용하면 초보 자바 프로그래머들도 XML을 관리할 수 있다. JAXP를 사용하는 고급 개발자들은 그들이 사용하는 바로 그 API에 대한 그릇된 개념을 갖고 있다.

여러분이 SAX와 DOM에 대해 기초 지식이 있다는 것을 전제 하에 이 글을 진행하겠다. XML 파싱이 처음이라면 온라인 소스나 필자의 책을 통해 SAX와 DOM에 대해 먼저 읽어보기 바란다.(참고자료) 콜백이나 DOM Node에 대해 정통할 필요는 없지만 적어도 SAX와 DOM이 API를 파싱한다는 것은 이해해야 한다. 이 둘의 차이점도 이해하면 도움이 된다.

JAXP: API인가 추상화인가?

엄밀히 말해서 JAXP는 API이지만 보다 정확히 말해서 추상화 레이어라고 불린다. 이는 새로운 방식의 XML 파싱을 제공하지도 않고, SAX 또는 DOM에 추가하지도 않으며 자바와 XML 핸들링에 새로운 기능을 제공하는 것도 아니다. (이것을 못 믿겠다면 다른 글을 찾아보기 바란다.) 대신 JAXP는 DOM과 SAX를 쉽게 사용할 수 있도록 하여 몇 가지 어려운 일들을 처리한다. 또한 DOM과 SAX API를 사용할 때 겪는 벤더 스팩의 작업들을 핸들 할 수 있다. 벤더 중립적이다.

Going bigtime

이전 버전의 자바 플랫폼에서, JAXP는 핵심 플랫폼에서 개별적으로 다운로드 받았다. Java 5.0에서, JAXP는 자바의 고정 부분이 되었다. 최신 버전의 JDK가 있다면(참고자료) 이미 JAXP를 갖고 있는 것이다.

SAX, DOM, 또는 기타 XML 파싱 API 없이는 XML을 파싱할 수 없다. SAX, DOM, JDOM, dom4j를 JAXP와 비교해 달라는 많은 요청을 받았지만, 비교 자체가 불가능하다. 위 네 개의 API는 JAXP와는 완전히 다른 목적이 있기 때문이다. SAX, DOM, JDOM, dom4j 모두는 XML을 파싱한다. JAXP는 이들 파서에 접근하는 방식과 그들이 노출하는 데이터를 제공하지만 XML 문서를 파싱하는 새로운 방식을 제공하는 것은 아니다. JAXP를 정확히 사용하려면 이러한 차이를 이해하는 것이 중요하다. 게다가 많은 XML 개발자들 앞에 놓여진 길은 여전히 멀다.

아직도 모호하다면 JAXP 배포판이 있는지 확인하라.(Going bigtime 참조) 웹 브라우저를 실행하고 JAXP API 문서를 로딩한다. javax.xml.parsers 패키지에 있는 API의 파싱 부분을 검색한다. 놀랍게도, 단 여섯 개의 클래스만을 발견하게 된다. 이것이 API가 되기까지는 얼마나 어려운가? 이 클래스들 모두는 기존 파서의 상단에 있다. 이들 중 두 개는 에러 핸들링 용이다. JAXP는 사람들이 생각하는 것 보다 훨씬 더 단순하다. 그렇다면 혼란은 왜 생기는 것일까?

Sitting on top of the world

JDOM과 dom4j(참고자료)도 JAXP 처럼 다른 파싱 API들보다 우위에 있다. 비록 두 API가 SAX 또는 DOM에서 데이터에 액세스 하는 다른 모델을 제공하지만 내부적으로 SAX를 사용하여 사용자에게 제공하는 데이터에 접근한다.

Sun의 JAXP와 Sun의 파서

파서/API 혼돈은 SUN이 JAXP와, JAXP가 기본적으로 사용하는 파서를 패키지하는 방식에서 기인한다. 이전 버전의 JAXP에서, SUN은 JAXP API(내가 막 언급했던 여섯 개의 클래스 포함)과 Crimson이라고 하는 파서를 포함시켰다. Crimson은 com.sun.xml 패키지의 일부였다. JDK에 포함된 신 버전의 JAXP에서, SUN은 Apache Xerces 파서(참고자료)를 재패키지 했다. 두 경우 모두, 파서는 JAXP API의 일부가 아닌 JAXP 배포판의 일부이다.

다음과 같은 방식으로 생각해보자: JDOM이 Apache Xerces 파서와 함께 출시된다. 이 파서는 JDOM의 일부는 아니지만 JDOM에 의해 사용되기 때문에 JDOM을 그 박스에서 사용할 수 있다는 의미가 된다. 같은 원리가 JAXP에도 적용되지만 명확하지는 않다: JAXP는 파서와 함께 제공되어 즉시 사용될 수 있다. 하지만 많은 사람들은 JAXP API의 일부로 SUN의 파서에 포함된 클래스를 참조한다. 예를 들어, 뉴스그룹에서는 다음과 같이 질문한다. “JAXP와 함께 나온 XMLDocument 클래스를 어떻게 사용하나요? 대답은 다소 복잡하다.

(패키지) 이름에는 무엇이 있나?

자바 1.5에 소스 코드를 보았을 때 내가 본 것에 놀랐다. 일반 패키지, org.apache.xerces에서 Xerces를 찾을 수 없었고 SUN은 Xerces 클래스를 com.sun.org.apache.xerces.internal로 옮겼다. (정당하지 못한 처사라고 생각했지만, 어떤 누구도 나에게 묻지 않았다.) 어떤 경우라도, JDK의 Xerces를 찾는 다면 거기에서 찾아라.

우선, com.sun.xml.tree.XMLDocument 클래스는 JAXP의 일부가 아니다. 이것은 SUN의 Crimson 파서의 일부로서 JAXP의 이전 버전에 패키지 되었다. 따라서 질문은 시작부터 잘못되었다. 둘째, JAXP의 주요 목적은 파서를 다룰 때 벤더 독립성을 확립하는 것이다. JAXP를 사용하면, SUN의 XML 파서, Apache의 Xerces XML 파서, Oracle의 XML 파서로 같은 코드를 사용할 수 있다. SUN 스팩의 클래스를 사용하면 JAXP를 위반하게 된다. 이 주제가 어떻게 얽혔는지를 보고싶은가? JAXP 배포판의 파서와 API는 일괄적으로 취급되고 몇몇 개발자들은 어떤 것의 클래스와 기능을 다른 것의 일부로 보는 실수를 한다.

이제 모든 혼돈을 뛰어넘어 코드와 개념 부분으로 넘어가 보자.


SAX로 시작하기

SAX는 이벤트 중심으로 XML을 처리하는 방식이다. 많은 콜백들로 구성된다. startElement() 콜백은 SAX 파서가 엘리먼트의 오프닝 태그를 만날 때 마다 호출된다. characters() 콜백은 캐릭터 데이터에 호출되고, endElement()는 엘리먼트의 엔드 태그에 호출된다. 더 많은 콜백들이 문서 프로세싱, 에러, 어휘 구조를 위해 제공된다. SAX 프로그래머는 그러한 콜백을 정의하는 SAX 인터페이스들 중 하나를 구현한다. SAX는 모든 콜백을 구현하고 모든 콜백 메소드의 디폴트 구현을 제공하는 (org.xml.sax.helpers 패키지에 있는) DefaultHandler 라고 하는 클래스를 제공한다. (Dealing with DOM 섹션에서 DOM에 대한 논의 시 중요하게 다루어진다.) SAX 개발자는 이 클래스를 확장하여 특정 로직의 삽입을 필요로 하는 메소드를 구현하면 된다. 따라서 SAX의 핵심은 다양한 콜백에 코드를 제공하여 파서가 적절히 이들을 실행할 수 있도록 하는 것이다. 다음은 전형적인 SAX 루틴이다:

  1. 특정 벤더의 파서 구현을 사용하여 SAXParser 인스턴스를 만든다.
  2. 콜백 구현을 등록한다. (DefaultHandler를 확장하는 클래스를 사용한다.)
  3. 파싱을 시작하고 콜백 구현이 실행되는 것을 관찰한다.

JAXP의 SAX 컴포넌트는 이 모든 것을 할 수 있도록 간단한 방법을 제공한다. JAXP 없이 SAX 파서 인스턴스는 벤더 클래스에서 직접 인스턴스화 되거나 org.apache.xerces.parsers.SAXParser, XMLReaderFactory (org.xml.sax.helpers 패키지에 있음.)라고 하는 SAX 헬퍼 클래스를 사용해야 한다. 첫 번째와 관련한 문제는 분명하다: 벤더 중립적이 아니다. 두 번째 것과 관련한 문제는 팩토리가, 인자로서, 파서 클래스의 String 이름이 사용되어야 한다. (Apache class, org.apache.xerces.parsers.SAXParser) 다른 파서 클래스를 String으로 전달하여 파서를 변경할 수 있다. 이 같은 접근 방식에서, 파서 이름을 변경하면 중요한 문장을 변경할 필요는 없지만 클래스를 재컴파일 해야 한다. 최선의 솔루션은 아니다. 클래스를 재컴파일 하지 않고 파서를 변경하는 것이 훨씬 쉽다.

JAXP는 더 나은 제안을 제시한다. 자바 시스템 속성으로서 파서를 제공할 수 있다. 물론 SUN에서 배포판을 다운로드 하면 SUN 버전의 Xerces를 사용하는 JAXP 구현을 얻을 수 있다. Oracle 파서로 파서를 변경하려면 classpath 설정을 변경하고 한 개의 파서 구현에서 다른 구현으로 옮겨야 한다. 하지만 코드 재컴파일은 하지 않아도 된다. 그리고 이것은 JAXP의 전부라고 할 수 있는 추상화의 마법이다.

SAX 개발자들에게

약간의 똑똑한 코딩으로 SAX 애플리케이션이 파서 클래스를 선택하게 하여 시스템 속성이나 속성 파일에서 사용하게끔 할 수 있다. 하지만 JAXP로는 이와 똑 같은 작동을 어떤 것도 하지 않고 할 수 있다. 따라서 JAXP를 사용하는 것이 더 낫다.

SAX 파서 팩토리 연구

JAXP SAXParserFactory 클래스는 파서 구현을 쉽게 변경할 수 있는 열쇠이다. 이 클래스의 새로운 인스턴스를 만들어야 한다. 새로운 인스턴스가 만들어진 후에 팩토리는 SAX 기능의 파서를 얻을 수 있는 메소드를 제공한다. 보이지 않는 곳에서 JAXP 구현은 벤더에 의존적인 코드를 살피면서 여러분의 코드가 오염되지 않도록 지킨다. 이 팩토리 다른 좋은 기능들을 갖고 있다.

SAX 파서의 인스턴스를 만드는 기본적인 작업 외에도, 이 팩토리를 사용하여 구성 옵션을 설정할 수 있다. 이 옵션들은 팩토리를 통해 얻어진 모든 파서 인스턴스들에 영향을 미친다. JAXP 1.3에서 가장 일반적으로 사용되는 두 개의 옵션은 setNamespaceAware(boolean awareness)를 인식하는 네임스페이스를 설정하는 것과 setValidating(boolean validating)로 DTD 밸리데이션을 작동하는 것이다. 일단 이 옵션들이 설정되면 이들은 메소드 호출 후 팩토리에서 얻어진 모든 인스턴스에 영향을 미친다.

일단 팩토리를 설정했다면 newSAXParser()를 호출하면 사용준비가 된 JAXP SAXParser 클래스의 인스턴스가 리턴된다. 이 클래스는 기저의 SAX 파서 (SAX 클래스 org.xml.sax.XMLReader의 인스턴스)를 래핑한다. 또한 어떤 벤더 스팩도 이 파서 클래스에 추가할 수 없도록 한다. (XmlDocument 클래스에 대한 논의 논의를 기억하는가?) 이 클래스는 실제 파싱 작동을 시작할 수 있다. Listing 1은 SAX 팩토리의 생성, 설정 사용 방법을 보여준다 :


Listing 1. SAXParserFactory 사용하기

import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

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

public class TestSAXParsing {
    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println ("Usage: java TestSAXParsing [filename]");
                System.exit (1);
            }
            // Get SAX Parser Factory
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            SAXParser parser = factory.newSAXParser();
            parser.parse(new File(args[0]), new MyHandler());
        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support " +
                               " the requested features.");
        } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining SAX Parser Factory.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyHandler extends DefaultHandler {
    // SAX callback implementations from ContentHandler, ErrorHandler, etc.
}

Listing 1에서, 두 가지의 JAXP 만의 문제들이 이 팩토리를 사용할 때 발생할 수 있다는 것을 알게 된다: SAX 팩토리를 얻거나 설정할 수 없음. SAX 파서를 설정할 수 없음. 첫 번째 문제는 FactoryConfigurationError로 나타나는데 JAXP 구현 또는 시스템 속성에서 지정된 파서가 얻어질 수 없을 때 발생한다. 두 번째 문제는 ParserConfigurationException으로 나타나고 요청된 기능이 사용중인 파서에서 사용할 수 없을 때 발생한다. 두 문제 모두 다루기 쉽고 JAXP를 사용할 때 특별한 부담을 주지 않는다. 사실 여러 기능들을 설정하는 코드를 작성하고 특정 기능을 사용할 수 없는 상황을 다루어야 할 것이다.

SAXParser인스턴스는 팩토리를 얻고 네임스페이스 지원을 끄고 밸리데이션을 키면 얻어진다; 그런 다음 파싱이 시작된다. SAX 파서의 parse() 메소드는 SAX HandlerBase 헬퍼 클래스의 인스턴스를 취한다. 이것은 커스텀 핸들러 클래스가 확장된 것이다. 코드를 보고 이 클래스의 구현과 전체 자바 Listing을 보라.(다운로드) File로 옮겨가 파싱한다. 하지만 SAXParser 클래스는 훨씬 더 많은 메소드를 포함하고 있다.

SAX 파서로 작업하기

일단 SAXParser 클래스의 인스턴스를 갖고 있으면 파싱을 위해 File에 전달하는 것 보다 많은 일을 할 수 있다. 큰 애플리케이션에서 컴포넌트들이 통신하는 방식 때문에 객체 인스턴스의 생성자가 사용자라고 가정하는 것이 언제나 안전한 것은 아니다. 한 개의 컴포넌트가 SAXParser 인스턴스를 만드는 동안 (또 다른 개발자가 코딩한)또 다른 컴포넌트는 같은 인스턴스를 사용해야 한다. 이러한 이유 때문에, JAXP는 메소드를 제공하여 파서의 설정을 결정하도록 한다. 예를 들어, isValidating()을 사용하여 파서가 밸리데이션을 수행할 것인지의 여부를 결정할 수 있고, isNamespaceAware()를 사용하여 파서가 XML 문서에서 네임스페이스를 처리할 수 있는지의 여부를 결정한다. 이러한 메소드들은 파서가 할 수 있는 일에 대한 정보를 제공하지만 SAXParser 인스턴스만을 가진 -그리고 SAXParserFactory 자체는 없는- 사용자들은 이러한 기능들을 변경할 방법이 없다. 파서 팩토리 레벨에서 이를 수행해야 한다.

문서의 파싱을 요청하는 다양한 방법들이 있다. File과 SAX DefaultHandler 인스턴스를 수락하는 대신 SAXParserparse() 메소드도 SAX InputSource, 자바 InputStream, 스트링 형식의 URL 등을 DefaultHandler 인스턴스와 함께 수락할 수 있다. 따라서 다양한 형식으로 래핑된 문서를 파싱할 수 있는 것이다.

마지막으로, 기저의 SAX 파서 (org.xml.sax.XMLReader의 인스턴스)를 얻어 이를 SAXParsergetXMLReader() 메소드를 통해 직접 사용할 수 있다. 기본 인스턴스를 얻으면 일반적인 SAX 메소드들을 사용할 수 있다. Listing 2는 SAXParser 클래스의 다양한 사용 예제들이다:


Listing 2. JAXP SAXParser 클래스 사용하기

// Get a SAX Parser instance
SAXParser saxParser = saxFactory.newSAXParser();
// Find out if validation is supported
boolean isValidating = saxParser.isValidating();
// Find out if namespaces are supported
boolean isNamespaceAware = saxParser.isNamespaceAware();
// Parse, in a variety of ways
// Use a file and a SAX DefaultHandler instance
saxParser.parse(new File(args[0]), myDefaultHandlerInstance);
// Use a SAX InputSource and a SAX DefaultHandler instance
saxParser.parse(mySaxInputSource, myDefaultHandlerInstance);
// Use an InputStream and a SAX DefaultHandler instance
saxParser.parse(myInputStream, myDefaultHandlerInstance);
// Use a URI and a SAX DefaultHandler instance
saxParser.parse("http://www.newInstance.com/xml/doc.xml",
                myDefaultHandlerInstance);
// Get the underlying (wrapped) SAX parser
org.xml.sax.XMLReader parser = saxParser.getXMLReader();
// Use the underlying parser
parser.setContentHandler(myContentHandlerInstance);
parser.setErrorHandler(myErrorHandlerInstance);
parser.parse(new org.xml.sax.InputSource(args[0]));

지금까지 SAX에 대해 이야기했다. 하지만 특징적이거나 놀라운 부분을 설명하지는 않았다. JAXP의 추가된 기능은 매우 미미하다. 특히 SAX가 개입된 부분에서 그렇다. 이러한 미미한 기능 때문에 코드는 이동성이 좋고 SAX 순응의 XML 파서를 사용하여 무료 또는 상용으로 사용할 수 있다. SAX와 JAXP를 사용하는데 이것 이상이 없다. 이미 SAX를 알고 있었다면 98 퍼센트 정도를 간 것이다. 새로운 클래스와 두 개 정도의 자바 예외를 배우기만 하면 된다. SAX를 사용해보지 않았다면 지금이라도 시작하라.


DOM 다루기

DOM에 도전하기 전에 휴식이 필요하다고 생각한다면 굳이 그렇게 하지 않아도 된다. DOM과 JAXP를 사용하는 것은 SAX와 거의 같다. 해야 할 일은 두 개의 클래스 이름과 리턴 유형을 변경하는 것이다. SAX가 어떻게 작동하는지와 DOM이 무엇인지를 이해한다면 아무런 문제가 없을 것이다.

DOM과 SAX의 주요한 차이는 API의 구조이다. SAX는 이벤트 기반의 콜백 세트들로 구성된 반면, DOM은 in-memory 트리 구조를 갖고 있다. SAX를 사용하면 (개발자가 직접 만들지 않는 한) 작업할 데이터 구조가 없다. 따라서 SAX로는 XML 문서를 변경할 수 없다. DOM은 이 기능을 제공한다. org.w3c.dom.Document 클래스는 XML 문서를 나타내고 엘리먼트, 애트리뷰트, 다른 XML 구조를 나타내는 DOM 노드들로 구성된다. 따라서 JAXP는 SAX 콜백을 실행할 필요가 없다. 파싱을 통해 DOM Document 객체를 리턴하는 책임만 있다.

DOM 파서 팩토리

DOM에 대한 기본적인 지식과 DOM과 SAX에 대한 차이를 이해하면 더 알아야 할 것도 없다. Listing 3의 코드는 Listing 1의 SAX 코드와 너무 비슷하다. 우선, DocumentBuilderFactory를 얻는다.(Listing 1SAXParserFactory와 같은 방식). 그런 다음 이 팩토리는 밸리데이션과 네임스페이스를 핸들 하도록 설정된다.(SAX와 같은 방식). 그런 다음, DocumentBuilder 인스턴스 (SAXParser와 유사)는 팩토리에서 검색된다. 그런 다음 파싱이 실행되고 결과 DOM Document 객체는 DOM 트리를 프린팅하는 메소드로 옮겨진다:


Listing 3. DocumentBuilderFactory 사용하기

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

// DOM
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDOMParsing {

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println ("Usage: java TestDOMParsing " +
                                    "[filename]");
                System.exit (1);
            }

            // Get Document Builder Factory
            DocumentBuilderFactory factory = 
                DocumentBuilderFactory.newInstance();

            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);

            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new File(args[0]));

            // Print the document from the DOM tree and
            //   feed it an initial indentation of nothing
            printNode(doc, "");

        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not " +
                               "support the requested features.");
        } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining Document " +
                               "Builder Factory.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void printNode(Node node, String indent)  {
        // print the DOM tree
    }

}

이 코드와 관련하여 (JAXP의 SAX처럼) 두 가지 문제가 생길 수 있다: FactoryConfigurationErrorParserConfigurationException. 각각 원인은 SAX 때와 같다. 문제는 구현 클래스에 나타나거나(FactoryConfigurationError), 제공된 파서가 요청된 기능을 지원하지 않는다.(ParserConfigurationException). DOM과 SAX의 유일한 차이는 DOM으로는 SAXParserFactory대신 DocumentBuilderFactory를, SAXParser대신 DocumentBuilder라는 점이다. (전체 코드 리스트를 볼 수 있다. 다운로드 참조)

DOM 파서로 작업하기

DOM 팩토리가 있다면, DocumentBuilder 인스턴스를 얻을 수 있다. DocumentBuilder 인스턴스에 사용할 수 있는 메소드는 SAX의 그것에 사용할 수 있는 것과 매우 비슷하다. 주요한 차이는 parse() 메소드의 변수들이 SAX DefaultHandler 클래스의 인스턴스를 취하지 않는다는 것이다. 대신 DOM Document 인스턴스를 리턴하여 파싱된 XML 문서를 나타낸다. 다른 차이점은 두 가지 메소드들이 SAX 계열의 기능에 제공된다는 점이다:

  • setErrorHandler(): SAX ErrorHandler 구현을 사용하여 파싱할 때 생기는 문제를 처리한다.
  • setEntityResolver(): SAX EntityResolver 구현을 사용하여 엔터티 문제를 해결한다.

Listing 4는 메소드 실행 예제이다:


Listing 4. JAXP DocumentBuilder 클래스 사용하기

// Get a DocumentBuilder instance
DocumentBuilder builder = builderFactory.newDocumentBuilder();
// Find out if validation is supported
boolean isValidating = builder.isValidating();
// Find out if namespaces are supported
boolean isNamespaceAware = builder.isNamespaceAware();
// Set a SAX ErrorHandler
builder.setErrorHandler(myErrorHandlerImpl);
// Set a SAX EntityResolver
builder.setEntityResolver(myEntityResolverImpl);
// Parse, in a variety of ways
// Use a file
Document doc = builder.parse(new File(args[0]));
// Use a SAX InputSource
Document doc = builder.parse(mySaxInputSource);
// Use an InputStream
Document doc = builder.parse(myInputStream, myDefaultHandlerInstance);
// Use a URI 
Document doc = builder.parse("http://www.newInstance.com/xml/doc.xml");

DOM 섹션을 읽기 지쳤는가? 여러분만 그런 것이 아니다. 직접 쓰는 나도 지친다. SAX에 대해 배운 것을 DOM에 적용하는 것은 매우 단순하기 때문이다.


밸리데이션 수행하기

Java 5.0 (그리고 JAXP 1.3) 에서, JAXP는 문서를 검사하는 새로운 방식을 도입한다. setValidating() 메소드를 SAX 또는 DOM 팩토리에 사용하는 대신, 밸리데이션은 새로운 javax.xml.validation 패키지 내의 여러 클래스에서 시작한다. W3C XML Schema, DTD, RELAX NG Schema, 기타 제한 모델 등을 포함하여 밸리데이션의 모든 뉘앙스에 대한 상세한 설명을 이 글에서 쓰고 싶었지만 새로운 밸리데이션 모델은 사용하기 쉽다.

중복이 언제나 좋은 것은 아니다!

setValidating(true)javax.xml.validation 패키지는 사용하지 말아야 한다. 중첩 에러가 생길 수 있고 그 중 대부분이 트래킹하기 힘들다. 기본적으로 false가 되는 setValidating()을 호출하지 않는 습관을 기르는 것이 최상이다. 대신 새로운 JAXP 밸리데이션 프레임웍을 사용한다.

우선, 제약조건 모델—디스크 상의 파일에 있음—을 JAXP가 사용할 수 있는 포맷으로 변환한다. 파일을 Source 인스턴스에 로딩한다. (Source는 Part 2에서 자세히 다루겠다.) 그런 다음 SchemaFactory를 만들어 SchemaFactory.newSchema(Source)를 사용하는 Schema를 로딩한다. 이러면 새로운 Schema객체가 리턴된다. 마지막으로 Schema 객체로 Schema.newValidator()와 함께 새로운 Validator 객체를 만든다. Listing 5를 참조하라:


Listing 5. JAXP 밸리데이션 프레임웍

DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File(args[0]));

// Handle validation
SchemaFactory constraintFactory = 
    SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source constraints = new StreamSource(new File(args[1]));
Schema schema = constraintFactory.newSchema(constraints);
Validator validator = schema.newValidator();

// Validate the DOM tree
try {
    validator.validate(new DOMSource(doc));
    System.out.println("Document validates fine.");
} catch (org.xml.sax.SAXException e) {
    System.out.println("Validation error: " + e.getMessage());
}

매우 간단하다. 이 코드를 직접 작성하거나 전체 리스팅을 다운로드 하라. ( 다운로드 참조).


파서 변경하기

JAXP 팩토리 클래스가 사용하는 파서를 변경하는 것은 쉽다. 파서를 변경한다는 것은 파서 팩토리를 변경한다는 의미이다. 왜냐하면 모든 SAXParserDocumentBuilder 인스턴스들은 이 팩토리에서 나오기 때문이다. 팩토리는 파서가 로딩되는 것을 결정하기 때문에 변경해야 하는 것도 팩토리이다. SAXParserFactory 인터페이스의 구현을 변경하려면 자바 시스템 속성을 javax.xml.parsers.SAXParserFactory로 설정한다. 속성이 정의되지 않으면 디폴트 구현(벤더가 지정한 파서)이 리턴된다. 같은 원리가 DocumentBuilderFactory 구현에도 적용된다. 이 경우 javax.xml.parsers.DocumentBuilderFactory 시스템 속성이 쿼리된다.


요약

이 글을 다 읽었다면 JAXP의 거의 모든 범위를 본 것이다:

  • 훅을 SAX에 제공하기
  • 훅을 DOM에 제공하기
  • 파서가 쉽게 변경될 수 있도록 하기

JAXP의 파싱과 밸리데이션 기능을 이해하려면 트릭이 필요하다. JAXP 작동에서 가장 어려운 부분은 시스템 속성을 바꾸고, 파서 또는 빌더 대신 팩토리를 통해 밸리데이션을 설정하고, JAXP가 아닌 것을 가려내는 것이다. JAXP는 대중적인 자바와 XML API 보다 유용한 플러그인 가능한 레이어이다. 코드 벤더에 중립적이며 파싱된 코드를 재컴파일 하지 않고 파서에서 파서로 변경할 수 있다. JAXP를 다운로드 하라. Part 2에서는 JAXP를 사용하여 XML 문서를 변형하는 방법을 설명하겠다.



다운로드 하십시오

설명이름크기다운로드 방식
Sample code for All about JAXPx-jaxp-all-about.zip5 KBFTP

다운로드 방식에 대한 정보


참고자료

필자소개

Brett McLaughlin은 O'Reilly Media, Inc.의 기자 겸 편집자이다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=XML, 자바
ArticleID=92632
ArticleTitle=JAXP의 모든 것, Part 1
publish-date=05172005
author1-email=brett@newInstance.com
author1-email-cc=dwxed@us.ibm.com

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.