XML-RPC Fault 문서 포맷

XML-RPC request 처리 도중 발생하는 오류에 대한 Response 문서 형식은
하나의 값을 갖는 fault 엘리먼트로 기술한다.

[code]
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value><string>No such method!</string></value>
   </fault>
</methodResponse>
[/code]

실제로 처리 실패에는 error code가 부여된 경우가 일반적이므로 아래와 같이 struct로 에러 코드와
에러 문구를 함께 전송할 수도 있다.
[code]
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>code</name>
               <value><int>26</int></value>
            </member>
            <member>
               <name>message</name>
               <value><string>No such method!</string></value>
            </member>
         </struct>
      </value>
   </fault>
</methodResponse>
[/code]
2010/04/26 12:09 2010/04/26 12:09
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC 데이타 형식과 표기 방법

XML-RPC의 데이터 타입
 Type  Value  Examples
int or i4 32-bit integers between - 2,147,483,648 and 2,147,483,647. <int>27<int>
<i4>27<i4>
double 64-bit floating-point numbers <double>27.31415</double>
<double>-1.1465</double>
Boolean true (1) or false (0) <boolean>1</boolean>
<boolean>0</boolean>
string ASCII text, though many implementations support Unicode <string>Hello</string>
<string>bonkers! @</string>
dateTime.iso8601 Dates in ISO8601 format: CCYYMMDDTHH:MM:SS <dateTime.iso8601>
20021125T02:20:04
</dateTime.iso8601>
<dateTime.iso8601>
20020104T17:27:30
</dateTime.iso8601>
base64 Binary information encoded as Base 64, as defined in RFC 2045 <base64>
SGVsbG8sIFdvcmxkIQ==
</base64>


배열 데이타는 data 엘리먼트를 포함하는 array 엘리먼트로 기술한다.
[code]
<value>
   <array>
      <data>
         <value><string>This </string></value>
         <value><string>is </string></value>
         <value><string>an </string></value>
         <value><string>array.</string></value>
      </data>
   </array>
</value>
[/code]

아래는 4개의 integer 값을 갖는 배열을 표현한다.
[code]
<value>
   <array>
      <data>
         <value><int>7</int></value>
         <value><int>1247</int></value>
         <value><int>-91</int></value>
         <value><int>42</int></value>
      </data>
   </array>
</value>
[/code]

Array 구조는 서로 다른 데이터 타입을 포함하는것을 허용한다.
[code]
<value>
   <array>
      <data>
         <value><boolean>1</boolean></value>
         <value><string>Chaotic collection, eh?</string></value>
         <value><int>-91</int></value>
         <value><double>42.14159265</double></value>
   </data>
   </array>
</value>
[/code]

다차원 배열의 경우 간단히 Array 를 중첩하여 표기한다.
[code]
<value>
   <array>
      <data>
         <value>
            <array>
               <data>
                  <value><int>10</int></value>
                  <value><int>20</int></value>
                  <value><int>30</int></value>
               </data>
            </array>
         </value>
         <value>
            <array>
               <data>
                  <value><int>15</int></value>
                  <value><int>25</int></value>
                  <value><int>35</int></value>
               </data>
            </array>
         </value>
      </data>
   </array>
</value>
[/code]
2010/04/26 11:59 2010/04/26 11:59
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC Request 문서 포맷

XML-RCP Request Body 예시
double 타입의 변수를 인자로 하는 circleArea 메소드를 호출하는 XML-RPC 문서의 예.
[code]
<?xml version="1.0"?>
<methodCall>
   <methodName>circleArea</methodName>
      <params>
         <param>
            <value><double>2.41</double></value>
         </param>
      </params>
</methodCall>
[/code]


XML-RPC 헤더
[code]
POST /target HTTP 1.0
User-Agent: Identifier
Host: host.making.request
Content-Type: text/xml
Content-Length: length of request in bytes
[/code]


XML-RPC request 문서 전체 예시
[code]
POST /xmlrpc HTTP 1.0
User-Agent: myXMLRPCClient/1.0
Host: 192.168.124.2
Content-Type: text/xml
Content-Length: 169
<?xml version="1.0"?>
<methodCall>
   <methodName>circleArea</methodName>
      <params>
         <param>
            <value><double>2.41</double></value>
         </param>
      </params>
</methodCall>
[/code]
2010/04/26 11:40 2010/04/26 11:40
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML-RPC Response 문서 포맷

[code]
<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><double>18.24668429131</double></value>
      </param>
   </params>
</methodResponse>
[/code]
XML-RPC response는 단 하나 parameter만을 갖는다.
파라미터는 하나 이상의 값을 리턴하기 위해 struct 나 array 형식일 수 있다.
파라미터는 필수항목으로 비록 처리하는 쪽 메소드의 리턴 값이 없을지라도
'정상처리'를 표시하기 위해 boolean의 true (1)을 파라미터로 표현해야 한다.


[code]
HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124
[/code]
XML-RPC의 경우 HTTP1.0 스펙을 지원하지만 HTTP1.1과도 호환이 된다.
Content-type의 경우 'text/xml' 형식만 허용한다.
response의 길이를 bytes 단위로 Content-Length 에 표기한다.


[code]
HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124

<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><double>18.24668429131</double></value>
      </param>
   </params>
</methodResponse>
[/code]
결과적으로 위와 같은 형태의 문서가 XML-RPC 서버로부터 XML-RPC 클라이언트로 전송되고 connection을
끊는다.

2010/04/26 11:36 2010/04/26 11:36
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

java.beans.XMLDecoder와 java.beans.XMLEncoder를 이용한 Java객체의 xml 변환

자바 객체와 XML간의 변환에관해서는 몇 가지 라이브러리들이 존재하고 있습니다만 이번 시간에는 Java SDK에
기본으로 포함되어 있는 java.beans.XMLDecoder와 java.beans.XMLEncoder를 이용하여 자바객체<->XML간
변환 방법을 알아보겠습니다.

java.beans.XMLDecoder와 java.beans.XMLEncoder클래스는 J2SE 1.4 버전부터 이용할 수 있습니다.

우선, XML로 변환하고 XML로부터 복원할  적당한 자바 클래스를 작성합니다.
[code]
/*
 * Setter와 Getter를 가진 전형적인 JavaBean 클래스입니다.
 */

package com.yunsobi.beanxmltranslate;

import java.awt.Point;
import java.util.ArrayList;

/**
 *
 * @author 신윤섭
 */
public class SampleBean {
    private int[] scores;
    private String name;
    private Point seat;
    private ArrayList entry;

    public int[] getScores() {
 return scores;
    }

    public void setScores(int[] scores) {
 this.scores = scores;
    }

    public String getName() {
 return name;
    }

    public void setName(String name) {
 this.name = name;
    }

    public Point getSeat() {
 return seat;
    }

    public void setSeat(Point seat) {
 this.seat = seat;
    }

    public ArrayList getEntry() {
 return entry;
    }

    public void setEntry(ArrayList entry) {
 this.entry = entry;
    }

}

[/code]
보시면 아시겠지만 int 배열과 String, Point, ArrayList 객체를 갖는 전형적인 자바빈 객체 하나를 만들었습니다.

이제, 이 SampleBean 클래스를 xml로 변환하고 변환한 xml로 부터 SampleBean객체를 복원하는 코드를 작성합니다.
[code]
/*
 * java.beans.XMLDecoder와 java.beans.XMLEncoder는
 * Java객체를 XML로
 * XML에서 Java 객체로 변환하는 쉬운 방법을 제공 합니다.
 */
package com.yunsobi.beanxmltranslate;

import java.awt.Point;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;

/**
 *
 * @author 신윤섭
 */
public class BeanXmlTranslator {

    public static void main(String args[]) throws Exception {
 
 //xml로 변환할 클래스를 생성
 SampleBean sample = new SampleBean();
 //파라메터를 세팅해 봅시다.
 sample.setScores(new int[]{100, 90, 75}); //int[]
 sample.setName("Gore"); //String
 sample.setSeat(new Point(5, 3)); //java.awt.Point
 //ArrayList에 String과 Point도 넣어보구요..
 ArrayList entry = new ArrayList();
 entry.add("우리는");
 entry.add("하나다");
 entry.add(new Point(1,1));
 sample.setEntry(entry);
 
 //자.. 객체를 XML로 변환 시킵니다.
 XMLEncoder encoder = new XMLEncoder(
     new BufferedOutputStream(
     new FileOutputStream("C:\\Sample.xml")));
 encoder.writeObject(sample);
 encoder.close();
 //객체 레퍼런스를 찍어보구요..
 System.out.println(sample);
 
 //이젠 XML에서 객체로 복원시켜 봅니다.
 XMLDecoder decoder = new XMLDecoder(
     new BufferedInputStream(
     new FileInputStream("C:\\Sample.xml")));
 SampleBean sample2 = (SampleBean) decoder.readObject();
 decoder.close();
 //객체 레퍼런스를 찍어보구요..
 System.out.println(sample2);
    }
}

[/code]
예.. 변환의 핵심은 XMLEncoder 클래스의 writeObject() 메소드와 XMLDecoder  클래스의 readObject() 메소드
에 있습니다.

객체를 xml로 변환한 결과를 한번 볼까요?
[code]
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_13" class="java.beans.XMLDecoder">
 <object class="com.yunsobi.beanxmltranslate.SampleBean">
  <void property="entry">
   <object class="java.util.ArrayList">
    <void method="add">
     <string>우리는</string>
    </void>
    <void method="add">
     <string>하나다</string>
    </void>
    <void method="add">
     <object class="java.awt.Point">
      <int>1</int>
      <int>1</int>
     </object>
    </void>
   </object>
  </void>
  <void property="name">
   <string>Gore</string>
  </void>
  <void property="scores">
   <array class="int" length="3">
    <void index="0">
     <int>100</int>
    </void>
    <void index="1">
     <int>90</int>
    </void>
    <void index="2">
     <int>75</int>
    </void>
   </array>
  </void>
  <void property="seat">
   <object class="java.awt.Point">
    <int>5</int>
    <int>3</int>
   </object>
  </void>
 </object>
</java>

[/code]
객체의 타입과 할당된 값이 깔끔한 xml문서로 작성된것을 확인 하실 수 있습니다.

자바에는 객체 직렬화(Serialize) 란게 있지요..
위 XMLDecoder와 XMLEncoder가 직렬화 기능을 이용하는건지 아닌지 확인 해 볼까요?
가장 정확한 방법은 XMLDecoder와 XMLEncoder의 소스를 보는것이겠지만 이미 위 예제 소스만으로도
충분히 예상 하실 수 있습니다. 위 예제의 SampleBean 이 Serializable 하지 않음에도 xml문서로 생성되었습니다.
예.. XMLDecoder와 XMLEncoder 내부적으로 java Reflection을 이용하여 xml, 객체간 변환을 수행하고 있습니다.

java Reflection을 이용한다고 했습니다. 이는 getXXX, setXXX 메소드가 없다면 해당 변수를 xml로 만들어내지
못하고 객체로 복원해 내지도 못함을 의미합니다.

또 하나, XMLEncoder를 통해 변환된 xml은 문서의 형태로 보아도 외부 시스템과의 정보의 교환보다는
객체의 상태저장/복원을 목적으로 사용하는게 더 어울릴거란 생각이 드네요.

2009/01/08 21:01 2009/01/08 21:01
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

About XSD (XML Schema Definition)


XSD(XML Schema Definition)?

 XML문서의 유효성을 검사하기위해 사용하는 DTD의 한계로 인해 나타난 새로운 표준.
 XML문서의 구조를 정의하며 XSD는 w3c.org에서 표준을 정했다. 그 모든 내용은
 http://www.w3.org/TR/xmlschema-0/ (Primer)
 http://www.w3.org/TR/xmlschema-1/ (Structure)
 http://www.w3.org/TR/xmlschema-2/ (Data Type)
 에서 확인 가능.
 
XML_XSD.ppt

동명대 컴공과 자료




XML Schema에 대한 온라인 튜토리얼 제공 사이트
http://www.w3schools.com/Schema/default.asp
 
XML문서로부터 XSD를 생성해 주는 커맨드라인 도구
http://thaiopensource.com/relaxng/trang.html

2008/06/13 13:34 2008/06/13 13:34
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

자바 프로그래밍으로 온라인 XML 데이터 검색하기

소스 파일 :



인터넷에 정보를 퍼블리싱하는 것이 보편화 되어 가면서 이러한 정보를 발견하고 요청하는 것 또한 자연스러운 일이 되었다. 이 글은 자바 프로그래밍을 사용하여 웹 기반 XML 데이터를 얻고 데이터를 파싱하여 필요한 엘리먼트와 속성들을 필터링하고 요청된 정보를 이용하여 작업을 수행하는 방법을 설명한다.

인터넷에 정보를 내보내는 것이 자연스러운 현상인 만큼 정보를 발견하고 요청하는 것도 자연스러운 일이 되었다. XML은 비즈니스와 소비자가 정보를 더욱 쉽게 공유할 수 있도록 데이터를 설명하는데 사용할 수 있는 기술 중 하나이다. 웹 상의 XML 정보의 예제에는 날씨 정보, 증권 시세표, 선적표, 항공요금표, 경매가격, 오늘의 유머 같은 정보들을 포함하고 있다. 이 데이터에 왜 액세스하려 하는가? 아마도 자기 지역의 날씨 데이터를 저장하고 검색하거나, 선적 과정을 조사 할 작은 유틸리티를 작성해야 할 이유에서 일 것이다. 여러분이 자바 개발자라고 가정한다면 파싱과 조작이 쉬운 XML 정보를 찾을것이다.

물론 HTML 페이지가 있지만 대부분의 데이터는 XML 포맷에서 비롯되고 웹 서버에는 HTML로 변환된다. 많은 웹 사이트들은 두 포맷으로 정보를 제공한다. 구 브라우저와 데이터를 단순히 검색하고자 하는 사람들에게는 HTML로 제공되고, 데이터 모음과 분석을 원하는 넷 정통 프로그래머에게는 XML 포맷으로 제공된다. HTML 페이지에서 데이터를 가져오는 것 보다는 자바 프로그래밍으로 XML 정보를 파싱하고 모으는 것이 훨씬 더 쉽다. 우선 표준 XML 파서가 있으면 다운로드하기가 쉽다. 둘째, 문서 구조는 시간이 흐르면서 변하기 때문에 HTML 태그 보다는 XML 엘리먼트와 속성으로 나타내는 것이 더욱 쉽다.

시작하기

XML 데이터를 파싱하기 위해서는 XML 파서가 있어야 한다. 물론 여러분 정도라면 각자의 파서를 작성할 수 있겠지만 완벽한 기능의 표준 파서들이 무료로 나와있다. Java 2 Platform, version 1.3 Enterprise Edition (J2EE)도 유용할 것이다. 파서는 개발자의 시각에서 볼 때 속도나 신뢰성에서 차이가 나지만 프로그래밍 모델과 인터페이스는 동일하다. 게다가 무료이다.

자바 개발자라면 javax.xml 패키지에 주의를 기울여야 한다. 이 패키지에는 XML 데이터를 파싱하는데 필요한 모든 코드가 있다. XML 문서를 다른 형식으로 변형하는 패키지와 함께 파서 구현 패키지들이 있다.






DOM vs SAX 파서

두 가지 유형의 XML 문서 파서가 있다. 이들은 XML 문서에 액세스하는 방식부터 다르다:

  • Document Object Model (DOM). XML 문서로의 랜덤 액세스(random access)에 사용된다. DOM의 장점은 메모리 안에 문서의 전체 모델을 갖고 있다는 점이다. 이는 모든 XML 엘리먼트에 어떤 순서로든지 액세스가 가능하다는 것을 의미한다. 하지만 큰 문서의 경우 둔해 질 수 있다. 메모리에서 실행시키지 않으면 시스템이 한계에 다다를 때 퍼포먼스가 느려진다.
  • Simple API for XML (SAX). 순차적 액세스(sequential access)에 사용된다. SAX의 장점은 문서의 한 부분이 메모리에서 사용될 수 있기 때문에 보다 큰 문서를 핸들할 수 있다는 점이다. SAX의 단점은 엘리먼트를 순서대로 처리해야 하며 한번에 볼 수 있는 문서 부분도 작다. SAX를 이용하여 문서를 파싱할 때 XML 부분을 저장할 수 있다.

온라인 정보 사이트(날씨 데이터 또는 주식 현황)에 액세스하는 XML 문서는 작고 집중되어 있는 경향이 있다. 일반적으로 특정 사이트나 주식 데이터를 검색하고 정보를 처리하고 다음 쿼리 또는 문서가 고려된다. 이러한 유형의 액세스의 경우 DOM은 선호되는 파싱 방식이다. 반면 SAX 파싱은 이 분야가 아니다.






XML 문서 얻기

파서가 있으므로 웹에서 XML 문서에 액세스 할 수 있다. Listing 1은 인터넷에서 날씨 데이터를 가져오는 예제이다:

Listing 1. XML 날씨 데이터 예제

<forexml site="Austin">
<observation
    city="Austin, Texas"
    temp.F="75"
    rel_hum.percent="31"
    wind_speed.knt="8"
    skies="partly cloudy"
/>
<almanac
    sunrise="7:08 AM"
    sunset="6:21 PM"
/>
<forecast
    type="nearterm"
    source="NWS"
    day="THIS AFTERNOON"
    weather="SU"
    high_temp="77"
    text="HIGHS 75 TO 80. WEST 15 TO 20 MPH."
/>
</forexml>

Unisys Corporation은 National Oceanic and Atmospheric Administration와 수백개의 날씨 관측 사이트와 협력하여 www.weather.unisys.com/forexml.cgi에 XML 포맷으로 기상 관측을 내보내고 있다. URL (예를 들어, www.weather.unisys.com/forexml.cgi?Austin) 다음에 요청 필드에 도시 이름 같은 관측 지역의 이름을 입력하라. 물음표는 요청 필드를 URL에 추가하는데 필요한 경계기호 이다. 읽을 수 있는 관측 결과가 XML 포맷으로 리턴된다. (Listing 1). J2EE에 제공된 XML 파서를 사용해보자.

개념상으로 XML 문서를 DOM으로 볼 수 있다. forexml 노드는 문서의 노드 트리에서 뿌리 노드 (root node)로서 보여진다. 뿌리 노드는 observation, almanac, forecast 같은 자식 노드를 갖고 있다. 이들 노드 각각 observation 이나 0 또는 그 이상의 엘리먼트를 갖고 있다. 예를들어 observation 엘리먼트는 75 값을 가진 temp.F 속성을 갖고 있다. 그림 1은 문서 노드의 계층을 나타낸다:

그림1. 문서 노드 계층(맑은 날!)
Hierarchy of document nodes (A pleasant day!)

주어진 XML 문서에서 관심있는 엘리먼트와 속성을 어떻게 필터링 할까? 우선 파싱하고자 하는 인자를 모아야 한다. Listing 2는 명령행에서 프로그램 인자를 모으고 그들을 processDocument라고 하는 메인 프로세싱 메소드로 전달하는 표준 main 메소드를 보여주고 있다. Java applet과 HTML 매개변수로 비슷한 작동을 수행할 수 있다. Listing 2에서 코드는 표준이고 XML 프로그래밍이 없다. 다음에 온다.

Listing 2. 날씨 애플리케이션 메인 메소드

// Given a list of arguments, parse a city
// name and a list of attributes to filter.
// Alternatively, accept "all" as an indication
// to get all attributes

public static void main( String [] args ) {
   if ( args.length == 0) {
     System.out.println
       ( "Usage: java Weather city"
           +" [all|attributes]" );
     System.out.println
       ( "Example:"
           +" java Weather Austin temp.F" );
     System.exit( 0 );
   }
   // Gather arguments
   String city = args[ 0 ];
   String [] attrNames = null;
   if ( args.length == 1) {
     attrNames = new String [] { "all" };
   } else {
     // args.length > 1
     attrNames =
       new String [ args.length - 1 ]; 
     for ( int i = 1; i < args.length; i++)
       attrNames[ i - 1 ] = args[ i ];
   } // if
	
   // Process document
   processDocument( city, observationTagName,
     attrNames );
} // main

Listing 3은 XML 문서를 얻어 이를 파싱하는데 쓰이는 전형적인 구조이다:

Listing 3. DocumentBuilder 요청하기

// Given a city string, an element name, and
// a list of attributes, print the values of
// the attributes found in the XML document.
public static void processDocument(
   String city,
   String elementName, String [] attrNames ) {
   DocumentBuilderFactory dbf =
      DocumentBuilderFactory.newInstance ( ) ;
      
   try {
         DocumentBuilder db =
            dbf.newDocumentBuilder ( ) ;
            
         if ( city != null ) {
             String urlString =
                 weatherSite + city;
             Document document = getDocument
                ( db, urlString );
             if ( document != null ) {
                Node [] matchNodes =
                   getAttributes( document,
                      elementName, attrNames );
                 if (null != matchNodes ) {
                   if ( matchNodes.length > 6 )
                       printNodes( "City=" + city,
                         matchNodes, true );
                   else  
                       printNodes( "City=" + city,
                         matchNodes, false );
                 } else 
                     System.out.println (  
                        "Element \"" +
                        elementName +
                   "\" not found in document." );
         } else
             System.out.println 
                ( "No XML created from URL="
                   + urlString );
   } // if         
  } catch (  
      ParserConfigurationException e ) { 
      e.printStackTrace ( ) ; 
  }
} // processDocument

먼저 DocumentBuilderFactory의 인스턴스를 얻는다. 일단 팩토리 객체를 얻으면 새로운 Document Builder 메소드를 사용하여 DocumentBuilder를 얻는다. 이것은 XML 인풋 스트림을 파싱할 수 있는 객체이다. 리턴되는 실제 DocumentBuilderFactory 객체는 다음 설정에 의존한다:

  • javax.xml.parsers.DocumentBuilder Factory system property
  • JAVA_HOME/lib/jaxp.properties
  • META-INF/services/javax.xml.parsers.DocumentBuilderFactory 서비스(jar file)
  • platform default

J2EE를 설치할 때 디폴트 설정을 사용한다면 이 예제는 올바르게 작동한다 특별한 파서 기능을 이용하고자 할 때에는 다른 파서를 지정한다. 여기서 얻을 수 있는 교훈은 많은 자바 XML 프로세서를 가지고 작업하라는 것이다.

Listing 3 의 코드 나머지 부분은 속성과 일치하는 XML 문서를 찾아 프린트한다. 이것은 고급 메소드 이다. URL에서 문서를 얻고 필터링하고 속성을 프린트하는 것에 대한 설명은 다음 섹션에 있다.

Listing 4는 웹 에서 XML 문서를 얻고 Listing 3에서 getDocument 메소드를 검색하는 것을 보여주고 있다:

Listing 4. 요청된 URL에서 XML 문서 파싱하기

// Using the given document builder object,
// construct and return an XML DOM
// from the given URL.

public static Document getDocument
  ( DocumentBuilder db, String urlString ) {
   try {
     URL url = new URL( urlString );
     
     try {
       URLConnection URLconnection =
          url.openConnection ( ) ;
       HttpURLConnection httpConnection =
          (HttpURLConnection)
         URLconnection;
         
       int responseCode =
        httpConnection.getResponseCode ( ) ;
       if ( responseCode ==
        HttpURLConnection.HTTP_OK) {
           InputStream in =
            httpConnection.getInputStream ( ) ;
            
           try {
             Document doc = db.parse( in );
             return doc;
           } catch(
            org.xml.sax.SAXException e ) {
                e.printStackTrace ( ) ;
           }
        } else {
            System.out.println
               ( "HTTP connection response !=
               HTTP_OK" );
               
        } 
     } catch ( IOException e ) { 
          e.printStackTrace ( ) ;
     } // Catch
  } catch ( MalformedURLException e ) {  
      e.printStackTrace ( ) ;
  } // Catch
  return null;
} // getDocument

우선 URLConnection 은 URL 스트링에 열려있다. 날씨 사이트가 HTTP 요청을 통해서 작동하는 것을 알고 있기 때문에 단순한 URLConnectionHttpURLConnection에 캐스팅할 수 있다. 그런 다음, 서버에서 온 응답 코드를 테스트한다. 요청에 에러가 없다는 것을 서버가 표시하면 커넥션 스트림을 열고 응답을 XML 스트림으로 취급한다. DocumentBuilder 파스 메소드는 파싱하고 데이터 스트림을 javax.xml. 문서로 돌린다. 이 과정 중 어떤 것이라도 실패하면 메소드는 XML 문서 객체를 만들 수 없다는 것을 나타내는 null을 리턴한다.






원하는 엘리먼트를 필터링하기

여러 엘리먼트 태그들이 root 태그인 forexml 다음에 나왔다는 것을 기억하라:

  • observation. 날씨 관측 지역 및 장소, 온도, 습도, 풍속 등의 데이터 포함.
  • almanac. 관측 장소의 일출과 일몰 등의 데이터 포함.
  • forecast. 앞으로의 날씨 예상 포함.

Listing 5는 observation 태그를 사용하고 다른 태그들은 무시된다:

Listing 5. XML 엘리먼트와 속성 맞추기

// Given an XML document,
// return the values of all attributes
// for the given element.

public static Node [] getAttributes
   ( Document document,
    String elementName, String [] attrNames ) {
    
    // Get elements with the given tag name
    // (matches on * too)
    NodeList nodes = document.getElementsByTagName
       ( elementName );
    if ( nodes.getLength() < 1) {
      return null;
    }
    
    Node firstElement = nodes.item( 0 );
    NamedNodeMap nnm =
       firstElement.getAttributes ( ) ;
   
    if (nnm != null) {
      // Test the value of each attribute
      Node [] matchNodes = new Node
         [ attrNames.length ];
        
       for (int i = 0; i < attrNames.length; i++){
          boolean all =
            attrNames[ i ].equalsIgnoreCase("all");
           if (all) {
             // named node map
             int nnmLength = nnm.getLength();
             matchNodes = new Node[ nnmLength ];
             
             for ( int j = 0; j < nnmLength; j++){
                matchNodes[ j ] = nnm.item( j );
             }
             return matchNodes;
           } else {
               matchNodes[ i ] = nnm.getNamedItem
                 ( attrNames[ i ] );
               if ( matchNodes[ i ] == null ) {
                 matchNodes[ i ] =
                     document.createAttribute
                     ( attrNames[ i ] );
                  ((Attr)matchNodes[ i ]).setValue
                    ( unknownAttribute );
               }
           } // if
      } // for
      
      return matchNodes;
   } // if
   
   return null;
} // printDocumentAttrs

Document 클래스가 getElementsByTagName 메소드를 가지고 있는 것에 주목하라. observation 엘리먼트 이름을 사용하여 메소드는 문서의 다른 엘리먼트에서 요청된 태그를 필터링하고 NodeList 객체를 리턴한다.

코드는 첫 번째 엘리먼트를 선택하고 getAttributes을 사용하여 그 엘리먼트용 XML 속성 리스트를 얻는다. 속성은 NamedNodeMap 객체에 리턴된다. 이러한 각각의 노드들은 요청된 속성 이름이 맞는지 또는 와일드 카드가 모두와 맞는지를 확인하기 위해 테스트된다. 이 예제는 노드 객체들의 어레이를 구현하고 이것을 caller에게 리턴한다. 엘리먼트나 속성을 찾지 못하면 메소드는 null을 리턴한다.

자바 프로그래밍으로 온라인 XML 데이터를 조작하는 주요 단계는 다음으로 요약될 수 있다:

  1. DocumentBuilderFactory를 이용하여 XML 파서 요청하기
  2. DocumentBuilder를 이용하여 XML 파서 및 구현하기
  3. 요청된 엘리먼트와 속성 찾기

발견한 데이터를 어떻게 이용할 것인가는 이제 여러분에게 달려있다. 다음 섹션에서는 XML 데이터를 핸들링하는 방법을 설명하겠다.






XML 데이터의 프린팅

Listing 6은 노드들을 통해 반복하고 속성 데이터를 프린트하는 방법이다:

Listing 6. XML 속성 데이터를 프린트하는 메소드 예제

// Given a set of nodes,
// print the values of the nodes.
// The nodes may be given a title.
// The nodes may be printed on one line
// (grouped) or many.

public static void printNodes
  ( String title, Node [] nodes,
       boolean grouped ) {
       
       // Report the name value of each node
       System.out.print( title );
       
       if (grouped){
          System.out.println ( ) ;
       }
       // Walk through the nodes.
       for ( int i = 0; i < nodes.length; i++ ) {
           Node node = nodes[ i ];
           System.out.print
              ( grouped ? " " : ", " );
           System.out.print
              ( node.getNodeName() + "=" +
              node.getNodeValue() );
              
            if ( grouped ) {
               System.out.println ( ) ;
            }  
    } // for
    
    if (!grouped) {
        System.out.println ( ) ;
    }  
} // printAttributeValues

Listing 5 에서는 캡쳐하고자하는 XML 엘리먼트와 속성 데이터를 맞춰 노드 어레이를 만든다. 이러한 특정한 프린트 루틴은 한 라인 또는 여러 라인에 걸쳐 모든 속성들을 프린트할 수 있다.

이제 여행의 끝에 와 있다. Listing 7은 파싱하고 선택한 XML 속성 데이터 아웃풋 예제이다:

Listing 7. 자바 날씨 프로그램 실행에 따른 아웃풋 예제

City=Austin
   city=Austin, Texas
   latitude=30.30
   longitude=-97.70
   time=3 PM CDT ...
   temp.F=84
   temp.C=28
   rel_hum.percent=58
   wind.string=S at 11 knt
   skies=clear    

이 경우 데이터를 System.out 으로 프린트했지만 각자의 애플리케이션에서는 데이터를 데이터베이스에 저장할 수 있고 트랜드 분석이나 그래프 그리기도 가능하다. 이러한 날씨 관측은 매 시간 업데이트 되어 주기적인 점검이 가능하다.

이 프로그램을 사용하여 모든 관측 속성들을 찾거나 온도 또는 상대 습도, 위도와 경도 같은 서브셋도 요청할 수 있다!






결론

자바 프로그래밍을 이용하여 온라인 XML 데이터를 검색하는 방법을 설명했다. 가장 중요한 단계는 관심있는 내용이 수록되어있는 사이트를 찾는 것이다. 일단 사이트를 발견하면 데이터 추출 작업은 모든 XML 문서의 경우 같다. 우선 문서를 요청하고 그런 다음 파싱한다. 마지막으로 원하는 엘리먼트와 속성을 필터링한다. 표준 XML 파서를 이용하면 직접 작성하는 것 보다 강력한 툴을 가질 수 있다.



원문 출처 : http://www.ibm.com/developerworks/kr/library/j-xmljava/
2007/06/07 12:42 2007/06/07 12:42
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

XML문서를 네트워크를 통해 전달하고자 할 경우

XML문자열을 stream으로 그냥 쓰는것 보단

  1. private static void serialize(OutputStream os, Document doc) throws IOException {
  2.     OutputFormat format = new OutputFormat(doc);
  3.     XMLSerializer serial = new XMLSerializer(os, format);
  4.     format.setIndenting(true);
  5.     serial.serialize(doc);
  6. }

와 같이 세련되게~
2007/06/07 12:35 2007/06/07 12:35
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

JDOM과 XML 파싱

2007/05/30 10:29

서비 JAVA , ,

Java에서의 XML 가공을 간소화 해주는 JDOM

대부분의 개발자는 과거 XML 데이타 구조를 가공하기 위해 수많은 Java 라이브러리 중 하나를 이용해본 적이 있을 것이다. 그렇다면 JDOM(Java Document Object Model)은 무엇이며 개발자는 왜 JDOM을 필요로 할까?

JDOM은 Java에 최적화된 XML 데이타 가공을 위한 개방 소스 라이브러리이다. JDOM은 W3C(World Wide Web Consortium) DOM과 유사하기는 하지만, DOM을 기반으로 설계되거나 DOM을 모델링하지 않은 대안적인 문서 객체 모델이다. 가장 큰 차이점은 DOM은 언어 중립적으로 설계되었고 초기에 HTML 페이지의 JavaScript 가공에 주로 이용되었던 반면, JDOM은 Java 전용으로 설계됐기 때문에 메소드 오버로딩(method overloading), 컬렉션(collections), 리플렉션(reflection) 및 친숙한 프로그래밍 환경 등 Java의 기본 기능들을 활용한다는 데 있다. Java 프로그래머에게는 JDOM이 보다 자연스럽고 ‘알맞게’ 느껴질 것이다. 이는 언어 중립적인 CORBA(Common Object Request Broker Architecture)에 비해 Java에 최적화된 RMI(Remote Method Invocation) 라이브러리가 보다 더 자연스럽게 느껴지는 것과 유사하다고 할 수 있다.

JDOM은 jdom.org에서 구할 수 있으며, 개방 소스로 Apache 스타일(상용 친화적) 라이선스로 제공된다. JDOM은 공동 협력을 통해 설계 및 개발됐으며, 메일링 리스트에 등록된 가입자만도 3,000여 명에 이른다. 또한 이 라이브러리는 Sun의 JCP(Java Community Process)에 Java Specification Request(JSR-102)로 채택됐으며, 곧 공식 Java 사양으로 채택될 것으로 전망된다.

이 글에서는 JDOM의 기술적 측면에 대해 다룰 것이다. 먼저, 주요 클래스에 대한 정보를 소개하고, 이어 Java 프로그램에서 JDOM을 이용하는 방법에 대해 설명할 것이다.

ORACLE XML TOOLS

이 XDK (XML Developer Kit)는 오라클이 개발자를 위해 제공하는 무료 XML 툴 라이브러리입니다. 이 라이브러리에서는 JDOM과 함께 이용할 수 있는 XML 파서와 XSLT 변환 엔진이 제공됩니다. 이들 툴에 대한 자세한 정보는 Oracle XML 홈페이지인 oracle.com/xml에서 제공됩니다.

파서를 다운로드받으려면 "XDK for Java"로 명명된 XML Developer Kit를 찾은 뒤 좌측의 "소프트웨어" 항목을 클릭해 다운로드를 시작합니다. 다운로드 받은 파일을 열면 xalparserv2.jar 파일에 파서가 들어있습니다.

JDOM 및 기타 소프트웨어가 기본값으로 오라클 파서를 이용하도록 구성하려면 oracle.xml.jax.JXSAXParserFactory에 JAXP javax.xml.parsers.SAXParserFactory 시스템 속성을 설정해야 합니다. 이는 JAXP에 오라클 파서를 이용하겠다고 밝히는 것입니다. 가장 쉬운 방법은 다음의 명령줄을 이용하는 것입니다.

java -Djavax.xml.parsers.SAXParserFactory=
oracle.xml.jaxp.JXSAXParserFactory

혹은 아래와 같이 프로그래밍 방식을 이용할 수도 있습니다.

System.setProperty("jaxax.xml.parsers
.SAXParserFactory",
"oracle.xml.jaxp.JXSAXParserFactory");

오라클은 XDK외에도 Oracle9i Database Release 2에 고유 XML 리포지토리를 제공하고 있습니다. Oracle9i XML Database (XDB)는 고성능을 지닌 고유 XML 스토리지 및 검색 기술입니다. XDB는 W3C XML 데이타 모델을 Oracle9i Database로 완벽하게 수용하며 XML의 네비게이션 및 질의를 위한 새로운 표준 액세스 방법을 제공합니다. XDB를 이용할 경우 관계형 데이타베이스 기술의 모든 이점과 함께 XML 기술의 장점을 활용할 수 있습니다.

JDOM 패키지의 구조

JDOM 라이브러리는 6개 패키지로 구성되어 있다. 첫째, org.jdom 패키지에는 Attribute, CDATA, Comment, DocType, Document, Element, EntityRef, Namespace, ProcessingInstruction, Text 등 XML 문서와 그 컴포넌트를 나타내는 클래스들이 포함돼 있다. XML에 익숙한 개발자라면 클래스 이름만 봐도 이해가 될 것이다.

다음은 XML 문서를 생성하는 클래스를 담고 있는 org.jdom.input 패키지이다. 가장 중심적이고 중요한 클래스는 SAXBuilder이다. SAXBuilder는 수신되는 SAX(Simple API for XML) 이벤트를 참조해 이에 대응하는 문서를 구성함으로써 문서를 생성한다. 파일이나 다른 스트림으로부터 문서를 생성하고자 한다면 SAXBuilder를 이용해야 한다. SAXBuilder는 SAX 파서를 이용해 스트림을 읽은 뒤 SAX 파서 콜백에 따라 문서를 생성한다. 이 설계의 좋은 점은 SAX 파서의 속도가 빨라질수록 SAXBuilder도 빨라진다는 것이다. 그밖에 주요 입력 클래스는 DOMBuilder이다. DOMBuilder는 DOM 트리를 통해 문서를 생성한다. 이 클래스는 이미 존재하는 DOM 트리를 JDOM 버전으로 대신 사용하고자 할 경우 편리하다.

이러한 빌더의 잠재성에는 아무런 제한이 없다. 예를 들어, Xerces에는 SAX보다 더 낮은 수준에서 운용되는 XNI(Xerces Native Interface)가 있으므로 SAX를 통해 노출되지 않는 일부 파서 정보를 다루기 위해서 XNIBuilder를 사용하는 것이 적합할 수도 있다. JDOM 프로젝트를 지원해온 한 가지 대중적인 빌더는 ResultSetBuilder이다. 이 빌더는 JDBC ResultSet을 통해 SQL 결과를 다양한 구성의 요소(element)와 속성(attribute)을 가지는 XML 문서를 표현한다.

org.jdom.output 패키지에는 XML 문서를 출력하는 클래스가 포함돼 있다. 가장 중요한 클래스는 XMLOutputter이다. XMLOutputter는 파일, 스트림, 소켓으로 출력할 수 있도록 문서를 바이트 스트림으로 변환한다. XMLOutputter는 원시 출력, 가공 출력, 압축 출력 등을 지원하는 다수의 특별 구성 옵션을 가지고 있다. 이 클래스는 상당히 복잡하다. DOM Level 2에 아직도 이런 기능이 없는 것은 바로 이런 이유 때문일 것이다.

그 밖에 문서의 컨텐트를 기반으로 SAX 이벤트를 생성하는 SAXOutputter가 있다. 이 클래스는 모호해 보이기는 하지만 XSLT 변환시 매우 유용한데, 이는 문서 데이타를 엔진으로 전송하는 데 있어 SAX 이벤트가 바이트 스트림보다 훨씬 효율적인 방식이기 때문이다. 또한 문서를 DOM 트리 형식으로 표현하는 DOMOutputter도 있다. 그 밖에 수십 라인의 코드만으로 문서를 JTree로 보여주는 JTreeOutputter도 있는데, JTreeOutputter를 ResultSetBuilder와 함께 사용할 경우 코드 몇 라인만 추가하는 것만으로도 SQL 질의 결과를 트리 뷰로 나타낼 수 있다.

DOM과는 달리, JDOM에서는 해당 문서가 빌더에 구속되지 않는다는 점에 주목해야 한다. 따라서 데이타를 담는 클래스와 데이타를 구조화하는 다양한 클래스, 이 데이타를 사용하는 그 밖의 여러 클래스가 포함된 세련된 모델이 생성된다. 원하는 만큼 자유롭게 혼합해 사용할 수 있다.

org.jdom.transform 및 org.jdom.XPath 패키지에는 기본 XSLT 변환과 XPath 조회를 지원하는 클래스가 포함돼 있다.

마지막으로, org.jdom.adapters 패키지는 DOM 상호작용의 라이브러리를 지원하는 클래스를 포함하고 있는데, 이 패키지의 클래스를 호출할 필요가 전혀 없다. 이들 클래스가 존재하는 이유는 각 DOM의 구현 방식이 각각의 부트 스트래핑 작업 방식별로 서로 다른 함수 이름을 사용하기 때문이며, 이에 따라서 각 어댑터 클래스가 표준 콜을 파서 전용 콜로 번역한다. JAXP(Java API for XML Processing)는 어댑터 클래스가 과도하게 사용될 때의 문제점에 대한 대안으로서, 실제로 이들 클래스에 대한 요구를 감소시키는 역할을 한다. 그러나 모든 파서가 JAXP를 지원하는 것은 아니고, 또한 라이선스 문제 때문에 어디나 JAXP가 설치돼 있는 것도 아니기 때문에, 이러한 클래스들에 대한 필요성은 여전히 남아 있다.

문서의 생성

문서는 org.jdom.Documentclass에 의해 표현된다. 다음은 완전히 새로운 문서를 생성하는 경우이다.

// This builds: <root/>

Document doc = new Document(new Element("root"));

또한 파일이나 스트림, 시스템 ID, URL 등을 통해 문서를 생성할 수도 있다.

// This builds a document of whatever's in the given resource
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(url);

소수의 콜을 조합함으로써 간단한 JDOM 문서를 생성할 수도 있다.

// This builds: <root>This is the root</root>

Document doc = new Document();
Element e = new Element("root");
e.setText("This is the root");
doc.addContent(e);

파워유저라면 다양한 방법을 연속적으로 호출하는 'method chaining'선호할 것이다. 이 방식을 통해 여러 개의 메소드를 한 번에 호출할 수 있다. 다음은 method chaining의 예이다.

Document doc = new Document(
  new Element("root").setText("This is the root"));

다음은 JAXP/DOM를 이용해 동일한 문서를 생성하는 예이다.

// JAXP/DOM
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("root");
Text text = doc.createText("This is the root");
root.appendChild(text);
doc.appendChild(root);

SAXBuilder 이용하기

앞서 설명했듯이 SAXBuilder는 모든 바이트 근간 자원으로부터 문서를 생성하는 간단한 메커니즘을 제공한다. 입력 변수가 없는 기본 SAXBuilder() 생성자는 내부적으로 JAXP를 이용하여 SAX 파서를 선택한다. 파서를 변경하고자 할 때는 javax.xml.parsers.SAXParserFactory 시스템 속성을 파서가 제공하는 SAXParser Factory를 가리키도록 설정하면 된다. Oracle9i Release2 XML 파서의 경우 다음과 같이 실행하면 된다.

java -Djavax.xml.parsers.SAXParserFactory=

oracle.xml.jaxp.JXSAXParserFactory YourApp

Xerces 파서의 경우 다음과 같이 실행한다.

java -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp
.SAXParserFactoryImpl YourApp

만약 JAXP가 설치돼 있지 않다면 SAXBuilder는 Apache Xerces를 기본값으로 이용한다. SAXBuilder 인스턴스가 생성된 뒤에는 다음과 같은 몇 가지 속성을 빌더에 설정할 수 있다.

setValidation(boolean validate)
WebLOCATOR

개방 소스 JDOM 라이브러리: jdom.org

Java Servlet Programming (second edition), (제이슨 헌터(Jason Hunter) 저) (2001년 O'Reilly & Associates 출간): www.oreilly.com

이 메소드는 문서 생성 중 DTD(Document Type Definition)에 대해 검증할 것인지 여부를 파서에 알려준다. 기본으로 설정된 값은 false이다. 사용된 DTD는 문서의 DocType 내에서 참조된 것이다. 다른 DTD에 대해 검증하는 것은 아직 불가능한데, 이 기능을 지원하는 파서가 아직 없기 때문이다.

setIgnoringElementContentWhitespace(boolean ignoring)

위 메소드는 요소 컨텐트에서 ‘무시할 수 있는 여백(ignorable whitespace)’을 무시할 것인지 여부를 파서에 알려준다. XML 1.0 사양에 의하면, 요소 컨텐트의 여백은 파서에 의해 보존돼야 하지만 DTD에 대해 검증할 경우 문서의 특정 부분이 여백을 지원하지 않는다는 사실을 파서가 인식할 수 있기 때문에 이 영역의 여백은 ‘무시할 수’ 있다. 기본값으로는 해제 상태이다. 문서를 입력 때와 동일한 컨텐트를 출력하고자 할 때가 아니라면 ‘무시 가능’으로 사용하는 것이 일반적으로 성능상 바람직하다. 단, 이 플래그는 DTD 검증이 수행될 때만 유효하며 이때는 이미 검증 과정을 통한 성능 저하가 발생한 것이기 때문에 결국 이 메소드는 검증이 이미 이용되고 있을 경우에만 유용하다는 점을 유의해야 한다.

setFeature(String name, String value)

위 메소드는 기본 SAX 파서상에 기능을 설정하는 방법이다. 이 방법은 원시적인 호출 방식이기 때문에 이 방법을 이용할 때는 매우 신중해야 한다. 왜냐하면 특정 기능(예 : 네임스페이스 변경)을 잘못 설정할 경우 JDOM 작업이 중단될 수도 있기 때문이다. 게다가 파서 전용 기능에 의존할 경우 이식성을 제한할 위험이 있다. 이 콜은 스키마 검증을 선택할 때 가장 유용하다.

setProperty(String name, Object value)

위 메소드는 기본 SAX 파서상에 속성을 설정하는 방법이다. 이 방법 역시 원시 호출 방식으로, 위험한 동시에 특히 스키마 검증시 파워유저에게 유용한 방법이다. 다음 코드는 이 방법들을 조합해 검증 기능을 선택하고 여백 무시 가능 기능으로 설정한 뒤 JAXP 선택 파서를 이용해 로컬 파일을 읽게 된다.

SAXBuilder builder = new SAXBuilder();
builder.setValidation(true);
builder.setIgnoringElementContentWhitespace(true);
Document doc = builder.build(new File("/tmp/foo.xml"));

XMLOutputter를 이용한 문서 출력

문서는 다양한 포맷으로 출력될 수 있지만 가장 흔한 포맷은 바이트 스트림이다. JDOM에서는 XMLOutputter 클래스가 이 기능을 제공한다. 이 클래스의 기본 생성자는 문서에 저장된 원문 그대로 문서를 출력하려 한다. 아래 코드는 원문 그대로 문서의 내용을 파일에 출력하는 코드이다.

// Raw output
XMLOutputter outp = new XMLOutputter();
outp.output(doc, fileStream);

여백에 신경 쓰지 않아도 된다면 텍스트 트리밍을 선택해 약간의 공간을 절약할 수 있다.

// Compressed output
outp.setTextTrim(true);
outp.output(doc, socketStream);

사람 눈에 맞춰 문서의 인쇄 상태를 보기 좋게 만들려면 들여쓰기와 줄 바꿔쓰기를 추가하면 된다.

outp.setTextTrim(true);
outp.setIndent("  ");
outp.setNewlines(true);
outp.output(doc, System.out);

이미 여백을 통해 포맷된 문서에 위의 가공 기능을 다시 적용할 경우 트리밍을 선택해야 한다. 그렇지 않으면 이미 포맷된 상태에서 또다른 포매팅을 가하는 것이 돼 최종 출력 상태가 보기 흉하게 된다.

요소 트리의 네비게이션

JDOM은 요소 트리(element tree)의 네비게이션을 간편하게 해준다. 루트 요소를 호출하려면 다음 코드를 이용한다.

Element root = doc.getRootElement();
모든 자식 요소 리스트를 불러오는 방법은 다음과 같다.

List allChildren = root.getChildren();

주어진 이름의 요소만을 호출하려면,

List namedChildren = root.getChildren("name");

주어진 이름의 요소 중 첫 번째 요소만을 호출하려면 다음을 이용한다.

Element child = root.getChild("name");

getChildren() 콜을 통해 반환된 리스트는 모든 Java 프로그래머가 알고 있는 리스트 인터페이스의 구현인 java.util.List이다. 이 리스트에서 특기할 만한 것은 이것이 라이브 리스트라는 점이다. 리스트에 가해진 모든 변경사항은 원본 문서 객체에도 반영된다.

// Remove the fourth child
allChildren.remove(3);
// Remove children named "jack"
allChildren.removeAll(root.getChildren("jack"));
// Add a new child, at the tail or at the head
allChildren.add(new Element("jane"));

allChildren.add(0, new Element("jill"));

이러한 리스트를 통한 대치 방법을 이용하면, 수많은 별도의 방법들을 과도하게 사용하지 않고도 요소를 다양하게 가공할 수 있다. 그러나, 편의상 주로 이용하는 작업인, 마지막에 요소를 추가하거나 이름이 있는 요소들을 삭제하는 경우 요소 자체에 이미 동일한 메소드가 포함돼 있기 때문에 이 작업을 실행할 때는 리스트를 우선 호출할 필요가 없다.

root.removeChildren("jill");
root.addContent(new Element("jenny"));

JDOM의 또 다른 장점은 한 문서 내에서 혹은 여러 문서 사이에서 요소들을 이동하는 작업이 간편하다는 것이다. 이 때 몇 개의 문서간에 이동하든 관계없이 동일한 코드를 사용할 수 있다.

Element movable = new Element("movable");
parent1.addContent(movable);    // place
parent1.removeContent(movable); // remove
parent2.addContent(movable);    // add

DOM의 경우 요소의 이동이 JDOM에서만큼 쉽지 않은데, 이는 DOM에서는 요소들이 그들을 생성한 객체에 강하게 묶여 있기 때문이다. 따라서 문서간 이동시에는 DOM 요소가 직접 '임포트' 되어야 한다.

JDOM에서 한 가지 유념할 사항은, 요소를 다른 데 추가하기 전에 제거해야 한다는 점이다. 이렇게 해야 트리 내에서 순환이 발생하는 것을 막을 수 있다. detach() 메소드를 이용하면 분리/추가 작업을 라인 하나로 처리할 수 있다.

parent3.addContent(movable.detach());

요소를 다른 부모에 추가하기 전에 먼저 분리하지 않았을 경우, 해당 라이브러리는 Exception을 떨어트릴 것이다(정확하고 도움이 되는 오류 메시지와 함께). 또한 라이브러리는 요소에 스페이스와 같은 부적절한 문자가 포함되지 않도록 요소의 이름과 컨텐트를 확인한다. 또한 단일 루트 요소의 포함 여부, 일관적인 네임스페이스 선언 여부 및 주석과 CDATA 섹션에 금지된 문자열이 없는지 등 기타 여러 규칙도 검증한다. 이를 통해 가능한 한 프로세스 초기 단계에서 문서가 'well-formed' 인지 확인하는 과정이 이루어지게 되는 것이다.

요소 속성의 처리

요소 속성의 예를 들면 다음과 같다.

<table width="100%" border="0"> ... </table>

요소 참조를 통해, 어떤 이름의 속성 값이든 요소에 요청할 수 있다.

String val = table.getAttributeValue("width");

또한 타입 변환과 같은 특별 가공 작업을 위해 속성을 객체로 불러올 수도 있다.

Attribute border = table.getAttribute("border");
int size = border.getIntValue();

속성을 설정하거나 변경하려면 setAttribute()를 사용한다.

table.setAttribute("vspace", "0");

속성을 삭제하려면 removeAttribute()를 사용한다.

table.removeAttribute("vspace");

텍스트 컨텐트를 가진 요소의 예를 들면 다음과 같다.

<description>
  A cool demo
</description>

JDOM에서는 호출을 통해 텍스트를 직접 이용할 수 있다.

String desc = description.getText();

한 가지 유의할 점은, XML 1.0 사양에서는 여백의 보존이 요구되기 때문에 이 경우 '\n A cool demo\n'반환된다는 것이다. 그러나 실제 환경에서는 여백의 포매팅에 대해 크게 유념할 필요가 없으므로 가장자리의 여백을 무시하고 텍스트를 불러오는 편리한 방법이 있다.

String betterDesc = description.getTextTrim();

여백을 아예 없애고 싶다면 스페이스를 이용해 내부의 여백을 표준화하는 getTextNormalize() 메소드를 이용하면 된다. 이 메소드는 다음과 같은 텍스트 컨텐트에 이용할 때 편리한다.

<description>
  Sometimes you have text content with formatting
  space within the string.
</description>

텍스트 컨텐트를 변경하고자 할 때는 setText() 메소드를 이용한다.

description.setText("A new description");

텍스트에 포함된 특수 문자는 모두 문자로서 올바르게 해석되어 출력시에 적절한 의미를 유지하게 된다. 다음 콜을 예로 들어보자.

element.setText("<xml/> content");

내부 저장 영역은 이 문자열을 그대로 문자로 저장할 것이다. 이 컨텐트에 대한 함축적 파싱은 이루어지지 않는다. 출력시에는 다음과 같이 표현된다.

&lt;xml/&gt; content<elt>

이는 이전 setText() 콜의 의미론적 내용을 보존하기 위한 것이다. 따라서, 요소 내에 XML 컨텐트를 포함하고자 한다면 적절한 JDOM 자식 요소 객체를 추가해야 할 것이다.

JDOM에서는 CDATA 섹션을 처리할 수도 있다. CDATA 섹션은 파싱되어서는 안 될 텍스트 블록을 지시한다. 원래 CDATA 섹션은 &lt;와 &gt; 같은 에스케이프 문자열을 과도하게 사용하지 않고도 HTML이나 XML을 손쉽게 포함시킬 수 있게 해주는 문법적인 용어이다. 그러나 JDOM에서는 이를 객체화하여 사용한다. CDATA 섹션을 생성하려면 이 문자열을 CDATA 객체로 래핑하면 된다.

element.addContent(new CDATA("<xml/> content"));

JDOM의 장점은 getText() 콜이 문자열을 CDATA 섹션으로 나타낼 것인지 일일이 물어보지 않고 문자열을 반환한다는 점이다.

혼합 컨텐트의 처리

어떤 요소에는 여백, 주석, 텍스트, 자식 요소 등 수많은 항목들이 포함되어 있다.

<table>

  <!-- Some comment -->
  Some text
  <tr>Some child element</tr>
</table>

어떤 요소에 텍스트와 자식 요소가 모두 들어 있을 경우 ‘혼합 컨텐트’를 포함하고 있다고 한다. 혼합 컨텐트를 처리하는 것은 어려울 수도 있지만, JDOM을 이용하면 쉽게 처리할 수 있다. 텍스트 컨텐트를 불러오고 자식 요소를 네비게이션하는 기본 이용 사례는 간단하게 처리할 수 있다.

String text = table.getTextTrim();  // "Some text"
Element tr = table.getChild("tr");  // A straight reference

주석, 여백 블록, 프로세싱 명령어, 엔티티 참조 등이 필요한 고급 응용의 경우, 혼합 컨텐트를 리스트로서 직접 불러올 수 있다.

List mixedCo = table.getContent();
Iterator itr = mixedCo.iterator();
while (itr.hasNext()) {
  Object o = i.next();
  if (o instanceof Comment) {
    ...
  }
  // Types include Comment, Element, CDATA, DocType,
  // ProcessingInstruction, EntityRef, and Text
}

자식 요소 리스트와 마찬가지로 혼합 컨텐트 리스트를 변경할 경우 원래 문서에도 영향을 미치게 된다.

// Remove the Comment.  It's "1" because "0" is a whitespace block.
mixedCo.remove(1);

자세히 살펴보면, 여기에 Text 클래스가 포함돼 있다는 사실을 알 수 있다. JDOM은 내부적으로 Text 클래스를 이용해 문자열 컨텐트를 저장하는데, 이는 이 문자열이 부모를 갖도록 하고 XPath 액세스를 보다 쉽게 지원하도록 하기 위한 것이다. 원시 컨텐트 리스트에 액세스할 때 텍스트만을 불러오거나 설정할 경우라면 이 클래스에 대해 염려할 필요가 없다.

DocType, ProcessingInstruction, EntityRef 클래스에 대해 보다 자세한 정보는 jdom.org의 API 설명서를 참조하기 바란다.



원문 출처 : http://www.oracle.com/global/kr/magazine/webcolumns/2002/o52jdom.html

2007/05/30 10:29 2007/05/30 10:29
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다