반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html

  • DOM은 XML을 생성하고 변경할 수 있다.

XML DOM 트리의 생성과 변경

  • 새로운 XML을 생성하기 위해서는 org.w3c.dom.DOMImplementation을 구현한 클래스를 사용해야한다.
  • Xerces의 DOMImplementation구현 : org.apache.xerces.dom.DOMImplementationImpl
  • 생성 예

    DOMImplementation domImpl = new DOMImplementationImpl();
    Document doc = domImpl.createDocument(null, "rootElement", null);
    
  • Document 객체 생성시 파서의 Document 구현 클래스를 사용하면 DocType 이 생성되지 않는다. DOMImplementation 을 사용해서 새로운 XML DOM 트리를 생성해야 한다.
  • DOMImplementation.createDocument(1,2,3);
    • 첫번째 인자 : 문서의 루트 요소를 위한 네임스페이스
    • 두번째 인자 : 루토 요소
    • 세번째 인자 : DocType 클래스의 인스턴스.
  • DocType이 필요할 경우 DOMImplementation.createDocType() 사용.
  • 변경 예

    Element root = doc.getDocumentEelment();
    root.setAttribute("id", id); // id 속성의 추가
    
    Element nameElement = doc.createElement("name");
    Text nameText = doc.createTextNode("내용");
    nameElement.appendChild(nameText); //name 요소에 텍스트 값 추가
    root.appendChild(nameElement); // rootElement 요소에 name 요소 추가
    
  • 모든 노드의 생성은 Document 객체의 create* 메소드를 통해서 이뤄진다.
  • "appendChild()"는 자식 노드를 추가한다.

네임스페이스

  • DOM Level 2는 네임스페이스를 지원한다.
  • 네임스페이스를 위해 Node 인터페이스는 "getPrefix()"와 "getNamespaceURI()" 메소드를 제공한다.
  • Document.createElementNS() 네임스페이스를 지원하는 요소 추가.
  • 네임스페이스를 인식하는 각 메소드의 첫번째 인자는 "네임스페이스 URI"이고, 두번째 인자는 요소와 속성등의 QName이다. QName은 "ora:copyright" 와 같은 형태를 띈다.
  • "ora:copyright" 요소에서 getPrefix() : "ora" 리턴
  • 네임스페이스에 속하지 않는 요소에서 getPrefix() : null 리턴
  • 네임스페이스를 지정했을 때는 루트 요소에 xmlns 속성을 지정해야 한다.

DOM Level 2 - 순회(Traverse)

  • DOM 트리를 순회하는 기능을 제공한다.
  • "org.w3c.dom.traversal.DocumentTraversal" 인터페이스를 이용한다.
  • 일반적인 파서의 Document 구현 클래스는 DocumentTraversal 도 함께 구현한다.
  • NodeIterator

    NodeList descriptionElements =
    	root.getElementsByTagNameNS(docNS, "description");
    Element description = (Element)descriptionElements.item(0);
    
    // NodeIterator를 구한다.
    NodeIterator i = ((DocumentTraversal)doc)
    	.createNodeIterator(description, NodeFilter.SHOW_ALL,
    	new FormattingNodeFilter(), true);
    
    Node n;
    
    while ((n = i.nextNode()) != null) {
    	System.out.println("Search phrase found: '" + n.getNod eVal  ue() + "'");
    }
    
  • createNodeIterator(1, 2, 3, 4)
    • 첫번째 인자 : 순회할 노드 요소
    • 두번째 인자 : 상수 필터
      1. NodeFilter.SHOW_ALL : 모든 노드를 포함하여 순회
      2. NodeFilter.SHOW_ELEMENT : 요소만 순회
      3. NodeFilter.SHOW_TEXT : 텍스트 노드만 순회
    • 세번째 인자 : NodeFilter 구현 객체
    • 네번째 인자 : 엔티티 참조의 실제값을 분석할 것인가?
    • 두번째와 세번째 인자가 함께 나올 경우 두번째 인자 필터를 우선적용하고 그 결과를 다시 세번째 인자로 필터링한다.
  • NodeFilter
    • public short acceptNode(Node n); 을 이용해서 순회할 노드인지 여부를 결정한다.
      • 리턴값 NodeFilter.FILTER_SKIP : 필터로 들어온 노드는 건너 뛰고 그 자식노드를 계속 탐색
      • 리턴값 NodeFilter.FILTER_REJECT : 필터로 들어온 노드와 그 자식 모두 건너 뜀
      • 리턴값 NodeFilter.FILTER_ACCEPT : 필터로 들어온 노드 사용
    • 노드 필터 예

      class FormattingNodeFilter implements NodeFilter {
      	public short acceptNode(Node n) {
      		if (n.getNodeType() == Node.TEXT_NODE) {
      			Node parent = n.getParentNode();
      
      			if ((parent.getNodeName().equalsIgnoreCase("b")) ||
      				(parent.getNodeName().equalsIgnoreCase("i"))) {
      				return FILTER_ACCEPT;
      			}
      		}
      
      		return FILTER_SKIP;
      	}
      }
      
  • TreeWalker
    트리 뷰를 얻는다. 필터를 이용해 특정한 요소 등만 가진 트리를 생성해낸다.

범위(Range)

알 수 없는 DOM 구조에 새로운 컨텐트를 추가하거나 또는 컨텐트를 삭제, 복사, 추출해야 할 경우에 범위 모듈을 사용한다.

Wrong document Exception

잘못된 문서 예외(Wrong document Exception)은 서로 다른 문서의 노드들을 함께 사용하려 할 때 발생한다.

다른 문서의 노드를 현재 문서에 append하려면 importNode를 사용한다.
Element otherDocElement = otherDoc.getDocumentElement();
Element thisDocElement = thisDoc.getDocumentElement();

// 대상 문서에 노드 임포트
Element readyToUseElement =
    (Element)thisDoc.importNode(otherDocElement);

// 아무문제없이 노드 추가
thisDocElement.appendChild(readyToUseElement);



반응형
출처: 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"인 요소만 남게 된다.

반응형
출처: http://kr.blog.yahoo.com/kwon37xi/folder/3381246.html

Property

파서에 추가되는 인터페이스로서 파서에 특정 인터페이스를 추가하여 분석 기능을 추가한다(수행 동작이 인터페이스를 구현한 객체에 구현되어 있다). Property는 Feature와 마찬가지로 URI를 사용하여 구별하는데, http://xml.org/sax/properites/를 접두어로 가지며 lexical-hander 같은 구분자가 뒤에 따라온다.

  • XMLReader.setProperty(String propertyID, Object value)
  • XMLReader.setFeature(String featureID, boolean value)
  • Object XMLReader.getProperty(String propertyID)
  • boolean XMLReader.getFeature(String featureID)

Feature

파서가 수행해야 하는 동작을 나타내기 위한 플래그로서 파서가 수행 가능한 동작의 실행 여부를 표시한다(수행하는 동작은 이미 파서에 구현되어 있다). Feature는 URI를 사용하여 구분하는데 http://xml.org/sax/features/를 접두어로 가지며 뒤에 validation과 같은 구분자가 따른다. 즉, 유효성 검사와 관련된 Feature는 http://xml.org/sax/features/validation이라는 URI로 표시된다.

EntityResolver

  • org.xml.sax.EntityResolver
  • 엔티티를 분석하는 이벤트 핸들러

public InputSource resolveEntity(String publicID, String systemID)

  • XMLReader가 엔티티 참조를 만날 때마다 그 엔티티에 해당하는 공개ID와 시스템ID를 resolveEntity메소드에 넘겨준다.
  • resolveEntity() 메소드에 어떤 코드를 추가하더라도, 항상 기본적으로는 null 을 반환하도록 해야 한다.

DTDHandler

  • org.xml.sax.DTDHandler
  • DTDHandler 인터페이스는 XMLReader가 분석하지 못한 엔티티나 표기(Notation) 선언을 만나게 되면 이를 알려준다. 물론 이 두 가지 이벤트는 XML문서가 아니라 DTD에서 발생한다.
  • 유효성 검사를 하면서 동시에 이 핸들러를 사용하는 것은 피하는 것이 좋다.
  • DTDHandler를 등록했다고 해도 유효성 검사 Feature(http://xml.org/sax/features/validation)를 선언하지 않으면 유효성 검사를 하지 않는다.

public void notationDecl(String name, String publicID, String systemID)

표기 선언(Notation)을 만나면 호출된다.

public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName)

분석되지 않은 엔티티 선언을 만나면 호출된다.

org.xml.sax.helpers.DefaultHandler 클래스

  • 여러 핸들러(ContentHandler, ErrorHandler, EntityResolver, DTDHandler)를 모두 구현했다. 구현한 메소드는 아무런 작동도 하지 않는다.
  • 이것을 상속하여 클래스를 작성하면 불필요한 메소드를 뺀 핸들러 클래스를 만들 수 있다.
  • EntityResolver는 다른 핸들러 클래스와 별도로 분리하는 것이 좋다.

XMLFilter

  • 하나의 SAX Reader가 모든 것을 처리하도록 하는 대신 Reader가 특정 처리를 하고 다른 Reader에게 넘겨주는 작업을 반복적으로 수행하는 파이프라인.
  • org.xml.sax.helpers.XMLFilterImpl클래스를 상속하여 구현하고, 자기 부모로 XMLReader를 지정하면 된다.
  • 핸들러는 최종 필터에 등록해야 한다.
  • XMLFilterImpl 클래스는 기본적으로 5개의 인터페이스에 정의된 모든 메소드는 관련된 이벤트를 처리하지 않고 단순히 넘어가도록 구현돼 있다. XMLFilterImpl 클래스를 상속한 클래스는 필터가 처리하는 메소드만 오버라이딩하여 구현하면 된다.

XMLWriter

org.xml.sax.ext.LexicalHandler

  • 주석, 엔티티선언, DTD 선언 그리고 CDATA 영역과 같은 구문과 관련된 이벤트를 처리하는 메소드를 제공한다. ContentHandler는 이 구문 관련 이벤트를 기본적으로 무시한다.
  • 이것을 사용하기 위해서는 Property에 이 핸들러를 구현한 객체를 등록해야만 한다.

    reader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
    

public void startDTD(String name, String publicID, String systemID)

DTD 참조 또는 선언의 시작

public void endDTD()

DTD 참조 또는 선언의 끝

public void startEntity(String name)

엔티티 참조 시작

public void endEntity(String name)

엔티티 참조 끝

public void startCDATA()

CDATA 영역 시작

public void endCDATA()

CDATA 영역 끝

public void comment(char[] ch, int start, int length)

  • 주석
  • <!-- 와 --> 주석 구분자를 제외한 텍스트만을 받는다.

org.xml.sax.ext.DeclHandler

  • DeclHandler 핸들러는 요소 선언이나 속성 선언과 같은 DTD에서 발생하는 특정 이벤트를 처리하는 메소드를 정의 하며 아주 특별한 경우에만 사용된다.
  • 이것을 사용하기 위해서는 Property로 이 핸들러를 구현한 객체를 등록해야 한다.

    reader.setProperty("http://xml.org/sax/properties/declaration-handler", declHandler);
    

public void attributeDecl(java.lang.String eName, java.lang.String aName, java.lang.String type, java.lang.String mode, java.lang.String value)

속성의 선언을 처리한다. <!ATTLIST>

public void elementDecl(java.lang.String name, java.lang.String model)

요소의 선언을 처리한다. <!ELEMENT>

public void externalEntityDecl(java.lang.String name, java.lang.String publicId, java.lang.String systemId)

외부 자원을 참조하는 엔티티 선언을 처리한다. <!ENTITY>

public void internalEntityDecl(java.lang.String name, java.lang.String value)

내부에서 선언된 엔티티 선언을 처리한다.

+ Recent posts