JAXB - unmarshal & marshal


XML은 이제 데이터를 표현하는 표준이며, Java Object를 XML로 나열할 때 사용하는 다양한 XML 파싱 기술들이 개발되어왔고, 그 중에 많이 쓰이는 대표적인 기술이 SAXParser와 DOMParser이다. 하지만 프로그래머들이 즉각적인 테스트를 할 방법으로 필요한 기술을 원했고 이 경우에 사용하는 기술이 JAXB이다.  JAXB는 Java Architecture for XML Bind의 약자로 XML로 부터 Java Object를 직렬화 하는 Unmarshalling과 이 반대의 Marshalling을 수행 할수 있도록 해주는 API이다.




-Binding Compiler(xjc) : 사용자가 정의한 XML Schema를 가지고, Schema에 정의 된 데이터 모델을 Java Object로 표현 할 수 있는 Java interface 또는 classes를 생성해주는 컴파일러이다.

-Schema-Derived Classes&Interface: XML Schema에 정의된 데이터 모델을 Java Object로 표현 할 수 있는 Java Interface & Classes이다.

-marshal: Java Object를 XML문서로 변환

-Unmarshal: XML문서를 Java Object로 매핑해주는 역할.




xml schema 파일 작성



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.epril.com/sqlmap" 
    xmlns:tns="http://www.epril.com/sqlmap" elementFormDefault="qualified">
 
    <element name="sqlmap">
        <complexType>
            <sequence>
                <element name="sql" maxOccurs="unbounded" type="tns:sqlType"/>
            </sequence>
        </complexType>        
    </element>
    <complexType name="sqlType">
        <simpleContent>
            <extension base="string">
                <attribute name="key" use="required" type="string"/>
            </extension>
        </simpleContent>
    </complexType>
</schema>
cs


간단한 xml 스키마를 작성하였다. 이 스키마는 jdbc를 사용할 때, DAO클래스에 난무하는 SQL문을 별도의 XML 파일로 분리하여 DAO에서 간단히 이 XML에 정의된 SQL문을 XML파일을 언마샬링하는 별도의 클래스에서 주입받아 사용하기 위해 작성했던 예제에서 쓰던 XML스키마이다.




이 스키마를 해당 프로젝트 워크스페이스 루트 경로에 저장하고 밑의 cmd 명령어를 실행해준다.



우선 워크스페이스에 있는 해당 프로젝트 폴더까지 가준다. 그리고 xjc 명령어로 시작으로 -p com.web.jpa 는 schema-derived classes&interface가 저장될 패키지명을 fullname으로 써준다. 그리고 -d src는 이 패키지를 포함하는 classes&interface가 저장될 루트 경로를 정해준다.( 위의 src는 편의상 넣은 것이고, 별도로 해당 프로젝트의 코드들이 존재하는 패키지의 루트를 같은레벨로 넣어주면 될것 같다.)


실행 후에 ObjectFactory.java & package-info.java & Sqlmap.java & SqlTyple.java 가 생성된 것을 볼 수 있다.


1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<sqlmap xmlns="http://www.epril.com/sqlmap"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.epril.com/sqlmap ../../../../../sqlmap.xsd">
    <sql key="add">insert</sql>
    <sql key="get">select</sql>
    <sql key="delete">delete</sql>
</sqlmap>
cs

언마샬링을 할 xml 예제 파일이다.




Unmarshalling & Marshalling



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.web.jpa;
 
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
 
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
 
import org.aspectj.weaver.tools.cache.GeneratedCachedClassHandler;
 
public class Test {
 
    public static void main(String[] args) throws JAXBException, FileNotFoundException {
        // TODO Auto-generated method stub
        TestClass t= new TestClass();
        t.readSqlmap();
    }
    
}
 
class TestClass{
    public void readSqlmap() throws JAXBException, FileNotFoundException {
        String contextPath=Sqlmap.class.getPackage().getName();
        System.out.println(contextPath);
        JAXBContext context=JAXBContext.newInstance(contextPath);
        Unmarshaller um=context.createUnmarshaller();
        
        Sqlmap sqlmap=(Sqlmap)um.unmarshal(getClass().getResourceAsStream("sqlmap.xml"));
        
        List<SqlType> sqlList=sqlmap.getSql();
        
        Iterator<SqlType> it=sqlList.iterator();
        while(it.hasNext()) {
            SqlType s=it.next();
            System.out.println(s.getKey()+":"+s.getValue());
        }
        Marshaller ms=context.createMarshaller();
        ms.marshal(sqlmap, new FileOutputStream("marshall.xml"));
    }
}
 
cs


위의 코드는 언마샬링을 시작으로 마지막에 마샬링 기능으로 끝나는 코드이다.


String contextPath=Sqlmap.class.getPackage().getName(); => 이 코드는 Schema-Derived-Classes&Interface가 존재하는 contextPath를 얻어 내기위한 코드이다. 이 코드를 이용하여 JAXBContext 객체를 얻을 수 있다. (result : com.web.jpa)


JAXBContext context=JAXBContext.newInstance(contextPath); =>이 코드는 위의 "JAXB를 간략하게 설명한 그림"에서 Schema-Derived-Classes&Interface가 JAXB를 가르키고 있는 화살표에 해당하는 과정이라고 생각하면 된다. contextPath를 이용해 해당 클래스와 인터페이스를 읽어들여서 파싱에 필요한 정보를 얻어내는 과정이다.


Unmarshaller um=context.createUnmarshaller(); =>언마샬링에 필요한 언마샬러 객체를 생성한다. 


Sqlmap sqlmap=(Sqlmap)um.unmarshal(getClass().getResourceAsStream("sqlmap.xml"); => 우선 sqlmap.xml은 이 Test.java 와 같은 디렉토리에 있다는 가정하에 진행한다. 그렇기에 getClass().getResourceAsStream("sqlmap.xml")을 통해 Test.java와 같은 레벨에 있는 sqlmap.xml을 자원을 inputStream객체형태로 리턴을 받는다. 그리고 이 리턴받은 자원을 이용해 unmarshalling을 진행한다. unmarshal( )의 리턴타입은 Object이기에 이것을 Sqlmap클래스 타입으로 cast 해준다. Sqlmap & SqlType 클래스의 코드는 밑과 같다. 코드를 천천히 해석하다보면 전부다는 아니지만 대략적으로 이런 형태를 가졌구나 정도는 이해가 된다. Sqlmap은 SqlType의 sql 엘리먼트를 List 형태로 갖고 있는 형태이고, SqlType은 sql 엘리먼트의 타입을 보여주는 클래스이다. key&value 형태로 되있는 sql 엘리먼트라는 것이 짐작이 갈 것이다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//
// 이 파일은 JAXB(JavaTM Architecture for XML Binding) 참조 구현 2.2.8-b130911.1802 버전을 통해 생성되었습니다. 
// <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>를 참조하십시오. 
// 이 파일을 수정하면 소스 스키마를 재컴파일할 때 수정 사항이 손실됩니다. 
// 생성 날짜: 2018.06.03 시간 02:20:59 PM KST 
//
 
 
package com.web.jpa;
 
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
 
 
/**
 * <p>anonymous complex type에 대한 Java 클래스입니다.
 * 
 * <p>다음 스키마 단편이 이 클래스에 포함되는 필요한 콘텐츠를 지정합니다.
 * 
 * <pre>
 * <complexType>
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="sql" type="{http://www.epril.com/sqlmap}sqlType" maxOccurs="unbounded"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "sql"
})
@XmlRootElement(name = "sqlmap")
public class Sqlmap {
 
    @XmlElement(required = true)
    protected List<SqlType> sql;
 
    /**
     * Gets the value of the sql property.
     * 
     * <p>
     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a <CODE>set</CODE> method for the sql property.
     * 
     * <p>
     * For example, to add a new item, do as follows:
     * <pre>
     *    getSql().add(newItem);
     * </pre>
     * 
     * 
     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link SqlType }
     * 
     * 
     */
    public List<SqlType> getSql() {
        if (sql == null) {
            sql = new ArrayList<SqlType>();
        }
        return this.sql;
    }
 
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//
// 이 파일은 JAXB(JavaTM Architecture for XML Binding) 참조 구현 2.2.8-b130911.1802 버전을 통해 생성되었습니다. 
// <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>를 참조하십시오. 
// 이 파일을 수정하면 소스 스키마를 재컴파일할 때 수정 사항이 손실됩니다. 
// 생성 날짜: 2018.06.03 시간 02:20:59 PM KST 
//
 
 
package com.web.jpa;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
 
 
/**
 * <p>sqlType complex type에 대한 Java 클래스입니다.
 * 
 * <p>다음 스키마 단편이 이 클래스에 포함되는 필요한 콘텐츠를 지정합니다.
 * 
 * <pre>
 * <complexType name="sqlType">
 *   <simpleContent>
 *     <extension base="<http://www.w3.org/2001/XMLSchema>string">
 *       <attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </extension>
 *   </simpleContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "sqlType", propOrder = {
    "value"
})
public class SqlType {
 
    @XmlValue
    protected String value;
    @XmlAttribute(name = "key", required = true)
    protected String key;
 
    /**
     * value 속성의 값을 가져옵니다.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getValue() {
        return value;
    }
 
    /**
     * value 속성의 값을 설정합니다.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setValue(String value) {
        this.value = value;
    }
 
    /**
     * key 속성의 값을 가져옵니다.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getKey() {
        return key;
    }
 
    /**
     * key 속성의 값을 설정합니다.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setKey(String value) {
        this.key = value;
    }
 
}
 
cs



<언마샬링의 결과>



Marshaller ms=context.createMarshaller();
        ms.marshal(sqlmap, new FileOutputStream("marshall.xml"));


=>이 코드는 XML 스키마에 적합한 Sqlmap 형태의 객체(언마샬링한 객체를 그대로 재활용하였다.)를 marshal.xml 이라는 xml파일로 내보내는 코드이다.

    

<마샬링결과>


별도의 경로를 지정해주지 않았음으로 워크스페이스에 해당 프로젝트 폴더 루트에 저장이 된다. 


저희가 사용하는 xBatis 같은 경우에도 모든 sql문을 xml파일로 관리하여 connection관리 등 설정 또한 xml로 관리를 합니다. 그러한 것도 정확히 JAXB기술을 사용하는지는 알수 없으니 이와 비슷한 기술을 사용하여 xBatis를 사용하는 것으로 알고 있습니다.

이상으로 JAXB의 간단한 예제를 보여드렸습니다. 다음에는 JAXB와 동일한 기능을 하는 많은 OXM API들을 소개시켜드리겠습니다.(SAX,DOM포함)

posted by 여성게
: