[JAVA] JAXB(Marshalling, UnMarshalling) 사용하기(Feat. XML 파싱)
1. JAXB(Java Architecture for XML Binding)
- JAXB는 Java Object 를 XML 로 직렬화 해주거나, XML 을 Java Object 로 역직렬화 해주는 Java API 입니다.
- 마샬링(Marshalling) : Java Object -> XML
- 언마샬링(UnMarshalling) : XML -> Java Object
- 기본적으로 JDK6 ~ 9 버전에는 JAXB 가 내장되어 있어 라이브러리를 따로 추가할 필요가 없습니다.(JDK 11 부터는 Dependency 추가가 필요합니다)
- 최근 Object 는 관련해서는 대부분 JSON(JavaScript Object Notation)을 많이 사용하지만, XML 도 종종 사용되고 있습니다.
- JSON 을 다룰 때 사용되는 Jackson 라이브러리를 통해서도 XML 파싱은 가능하지만 해당 글에서는 JAXB 를 사용해보도록 하겠습니다.
2. JAXB Annotation
- @XmlRootElement
- XML 의 Root Element 명을 정의.
- ex) @XmlRootElement(name = "header")
- 마샬링 시에는 Element 이름을 정의하고, 언마샬링 시에는 이름에 해당하는 Element 속성 또는 Element 를 저장. - @XmlElement
- XML 의 Element 명을 정의.
-ex) @XmlElement(name = "body")
- 마샬링 시에는 Element 이름을 정의하고, 언마샬링 시에는 이름에 해당하는 Element 속성 또는 Element 를 저장. - @XmlType
- XML Schema 이름과 Namespace 를 정의.
- propOrder 속성을 사용하여 XML 표현 시 요소들의 표현 순서 정의 가능.
- ex) @XmlType(propOrder = {"header", "body"} - @XmlElementWrapper
- 다른 XML 요소들을 감싸는 역할.
- List 같은 컬렉션 객체들을 XML 변활할 때 사용. - @XmlTransient
- XML에 포함하지 않은 필드에 사용. - @XmlAttribute
- ID 필드가 요소 대신 속성으로 매핑되도록 정의.
3. JAXB 사용하기
- 예를 들기 위해 아래와 같은 XML 형식의 데이터가 있다고 가정하고 진행하겠습니다.
<?xml version="1.0" encoding="UTF-8"?>
<User>
<Header>
<TimeStamp>20230816105312</TimeStamp>
</Header>
<Body>
<UserName>황재원</UserName>
<UserJob>Developer</UserJob>
<UserBlog>http://dev-jwblog.tistory.com</UserBlog>
</Body>
</User>
3-0. JAXB 사용 준비하기
- 위에서 말씀드렸듯이 JDK6 ~ 9 버전에는 라이브러리가 내장되어 있어서 따로 추가할 필요가 없습니다.
JDK 11 을 사용하신다면 아래 Dependency 를 추가가 필요합니다.
// build.gradle
implementation 'javax.xml.bind:jaxb-api'
runtimeOnly 'com.sun.xml.bind:jaxb-impl:3.0.0'
implementation 'org.glassfish.jaxb:jaxb-runtime'
implementation 'javax.activation:activation:1.1.1'
- 위와 예제와 같은 형태의 XML 이 있다면 동일하게 객체를 생성합니다.
- UserXml.class
@XmlRootElement(name = "User")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "UserType", propOrder = {
"header",
"body"
})
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserXml {
@XmlElement(name = "Header", required = true, nillable = true)
private Header header;
@XmlElement(name = "Body", required = true, nillable = true)
private Body body;
}
- Header.class
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "header", propOrder = {
"timeStamp"
})
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Header {
@XmlElement(name = "TimeStamp", required = true, nillable = true)
private String timeStamp;
}
- Body.class
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "body", propOrder = {
"userName",
"userJob",
"userBlog"
})
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Body {
@XmlElement(name = "UserName", required = true, nillable = true)
private String userName;
@XmlElement(name = "UserJob", required = true, nillable = true)
private String userJob;
@XmlElement(name = "UserBlog", required = true, nillable = true)
private String userBlog;
}
3-1. 마샬링(JavaObject -> XML)
해당 객체를 생성하는 방법은 다양하지만, 해당 글에서는 Builder 를 사용하여 생성합니다.
- 아래는 마샬링 테스트 입니다.
- Marshalling Test(JavaObject -> XML)
public class UserXmlTest {
@Test
void test() {
Header header = Header.builder()
.timeStamp("20230816105312")
.build();
Body body = Body.builder()
.userName("황재원")
.userJob("Developer")
.userBlog("https://dev-jwblog.tistory.com")
.build();
UserXml userXml = UserXml.builder()
.header(header)
.body(body)
.build();
try {
// XML 변환[S]
// XML 파싱에 필요한 정보를 생성
JAXBContext context = JAXBContext.newInstance(UserXml.class);
// 마샬링에 필요한 객체 생성
Marshaller marshaller = context.createMarshaller();
// 마샬링 옵션을 통해 XML 결과를 보기 좋게 출력
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter stringWriter = new StringWriter();
marshaller.marshal(userXml, stringWriter);
// XML 변환[E]
System.err.println(stringWriter.toString());
} catch (JAXBException e) {
throw new RuntimeException(e);
}
}
}
- 테스트 결과는 아래와 같습니다.
3-2. 언마샬링(XML -> JavaObject)
- 반대로 위 예제 XML 데이터를 JavaObject 로 변환해보도록 하겠습니다.
테스트를 위해서 XML 파일을 읽어서 변환하도록 하지만, 만약 API 로 하여 String 으로 전달받아도 방법은 동일하고, unmarshaller.unmarshal() 메소드에서 받고자 하는 형태로 변환하시면 됩니다.
- 객체는 위와 동일하고 마샬링 대신 언마샬링을 사용합니다.
- UnMarshalling Test(XML -> JavaObject)
public class UserXmlTest {
@Test
void unMarshalling_test() {
try {
// XML 데이터
FileInputStream fileInputStream = new FileInputStream("C:\\marshall\\UserXml.xml");
// JavaObject 변환[S]
// XML 파싱에 필요한 정보를 생성
JAXBContext context = JAXBContext.newInstance(UserXml.class);
// 언마샬링에 필요한 객체 생성
Unmarshaller unmarshaller = context.createUnmarshaller();
UserXml userXml = (UserXml) unmarshaller.unmarshal(fileInputStream);
// JavaObject 변환[E]
System.err.println(userXml.toString();
} catch (Exception e) {
}
}
}
- 테스트 결과는 아래와 같습니다.
4. 번외
- JAXB API 를 이용하면 간단하게 XML 파싱이 가능합니다.
- 그러나, JaxbContext 를 초기화(생성)하고 마샬러, 언마샬러를 생성하는 부분에서 JaxbContext 를 1회 생성 후 재사용을 권장하고 있습니다.(Thread-Safe)
- 생성 비용이 적지 않기 때문에 마샬/언마샬을 수행될 때마다 매번 생성하는 것보다 한 번 생성하고 재사용하는 편이 좋습니다.
- 다만, 마샬러와 언마샬의 경우에는 스레드에 안전하지 않기 때문에 반대로 수행될 때마다 다시 생성하는 것을 권장하고 있습니다.
참고
- https://velog.io/@huk00j/JAXB-marshal%EB%A7%88%EC%83%AC-unmarshal%EC%96%B8%EB%A7%88%EC%83%AC