반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html
DOM(Document Object Model)은 모든 프로그래밍 언어와 개발 도구에서 사용하는 문서의 컨텐트 모델을 표현하기 위해 설계되었다. 각 언어별로 바인딩이 존재한다.
이 점은 장점이 될 수도 있지만, JAVA 고유의 편리한 기능을 사용할 수 없어, 단점으로 작용하기도 한다. JAVA 고유의 기능을 사용한 JDOM이 DOM보다 훨씬 편리하다.
  • DOM은 모든 면에서 트리 모델이다.
  • DOM은 XML문서 전체를 메모리에 저장하여 표현한다.
  • 각 트리는 org.w3c.dom.Node 인턴페이스를 기반으로 한다. 요소, 속성, 텍스트, PI, 주석 등 모든 것이 Node로 표현된다.
  • 요소의 텍스트도 하나의 트리로 간주된다. 그러므로 Element 노드의 텍스트를 구할 때 "getText()"와 같은 방식으로 구할 수 없고, 요소의 Text 자식 노드들을 구한뒤, 거기서 값을 가져와야한다.

SAX의 장/단점

  • SAX는 순차적이라 XML문서의 요소를 무작위로 접근할 수 없다.
  • 형제 요소를 처리하기 어렵다.
  • 메모리를 훨씬 적게 사용한다.

XML 파싱 : Xerces 기준

  • DOM에서는 문서를 완전히 분석하여 트리 구조가 생성되어야 XML 문서의 데이터를 사용할 수 있다.
  • DOM에서 문서를 분석한 결과는 org.dom.w3c.dom.Document 객체로 표현된다.
  • 다음과 같이 XML 문서를 파싱한다.

    import org.apache.xerces.parsers.DOMParser; // 파서 import
    import org.w3c.dom.*; // DOM 인터페에스 import
    ...
    
    DOMParser parser = new DOMParser();
    
    // Document 객체인 DOM 트리 구성
    parser.parse("document.xml");
    Document doc = parser.getDocument(); // DOM Document 객체 얻기
    

  • import org.apache.xerces.parsers.DOMParser
    • void parse(org.xml.sax.InputSource inputSource)
    • void parse(java.lang.String systemId)

Node

  • DOM의 장점중의 하나는 XML을 표현하는(Document 객체를 포함한) 모든 DOM 객체가 DOM의 Node인터페이스를 상속한다는 점이다.
  • Node.getNodeType() : 현재 노드의 타입(요소, 속성, PI 등등..)을 가리킨다.
  • Document.getDocumentElement() : 최상위 요소(Root Element) 노드
  • Node.getNodeName() : 노드의 이름. Text 노드(요소의 값)의 경우 이 노드 이름은 의미가 없다.
  • Node.getNodeValue() : 노드의 값. Element 노드의 경우 이 값은 의미가 없으며 자식 노드를 구해 그 중 Text노드의 값을 가져와야만 한다.
  • Node.getChildNodes() : NodeList 인스턴스 반환. 자식 노드 목록.
  • NamedNodeMap Node.getAttributes() : Element 노드의 경우에만 유효. 속성 목록을 반환한다.
  • NodeList.item(int) : 자식 Node들을 순서대로 반환받는다.

DOCTYPE, PI 등의 처리

최상위 엘리먼트보다 상위에 오는 DOCTYPE과 PI(처리 지시어; Processing Instruction)등을 처리하려면 Document 노드 단에서 자식 노드들을 얻어야만 한다.

NodeList 사용시 주의점!

이것은 Java와 XML 책에는 없는 내용이다. 내 경험상의 주의점이다.

다음과 같은 XML이 있을 때
<root>
  <child1>hello</child1>
  <child2>hi~</chil2>
</root>
위 XML을 파싱하여 "root" 엘리먼트 Node 객체에서 getChildNodes()를 했을 경우에 주의할 점이 있다.
이 상황에서 "child1" 노드 객체를 가져올 때
DOMParser parser = new DOMParser();

parser.parse("test.xml");

Document doc = parser.getDocument();

Element root = doc.getDocumentElement();

NodeList children = root.getChildNodes(); // root 의 자식 노드 얻기 -- !! 요주의 부분!!

Node child1Node = children.item(0); // child1 요소 얻기 

Node textNode = child1Node.getChildNodes().item(0); // child1의 텍스트 얻기

System.out.println("child1 : " + textNode.getNodeValue());
위와 같이 하면 제대로 값을 가져올 수 있을까?

답은 "가져올 수도 있고 못 가져올 수도 있으나, 십중팔구는 NullPointerException이 발생한다는 것이다."
NullPointerException이 발생한다면 그것은 textNode가 null이기 때문이다.
위와 같이 NodeList.item(int) 메소드를 사용할 경우에 root 요소와 child1 요소 사이의 공백이 Text 노드로서 읽힐 수도 있다. 이 경우 NodeList.item(0)가 리턴하는 노드는 child1 요소가 아니라 root요소의 Text 노드가 되는 것이다.
만약 root 요소와 child1요소 사이에 아무런 공백도 없거나 DTD 등으로 공백을 무시하도록 했다면 안 그럴 수도 있다.

그러므로 child1 요소를 명백하게 가져오려고 할 경우에는 root.getChildNodes()를 사용해서는 안된다. root 노드를 Element 객체로 캐스팅하고 Element.getElementsByTagName(String)으로 명백하게 가져와야 한다.
Element root = doc.getDocumentElement();

// 이 부분이 바뀌었다!!
NodeList children = root.getElementsByTagName("child1"); // root 의 자식 노드 얻기

Node child1Node = children.item(0); // child1 요소 얻기

Node textNode = child1Node.getChildNodes().item(0); // child1의 텍스트 얻기

System.out.println("child1 : " + textNode.getNodeValue());

위와 같이 하면 children 객체에는 요소 이름이 "child1"인 요소만 남게 된다.

+ Recent posts