[Java] JAXB-API 사용법

식빵·2023년 1월 25일
0

Java Lab

목록 보기
10/29
post-thumbnail
post-custom-banner

🍀 2가지 방식의 XML Parsing


Spring MVC 를 사용중이라면 XML 을 java 내에서 파싱해야 되는 경우가 종종 있습니다.
그때 사용할 수 있는 선택지는 크게 2가지입니다.

이 게시물에서는 JAXB API 에 대해 제가 공부했던 내용들을 기록해보려 합니다.




🍀 의존성 추가


먼저 사용을 위해서 maven ( 또는 다른 gradle ) 을 사용해서
라이브러리 의존성을 추가해줘야 합니다.

참고로 jdk 17 을 사용 중입니다.
jdk 버전에 따라서 다르게 dependency 를 받아야 할 수도 있습니다.

관련 글: https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception

<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>jakarta.activation</groupId>
    <artifactId>jakarta.activation-api</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>4.0.2</version>
</dependency>




🍀 실습


여기서는 코드나 예제가 부분적으로 잘라서 게시된 것입니다.
모든 코드를 보고 싶다면 깃허브에서 확인하실 수 있습니다.


1. 테스트용 XML 생성

<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE>
    <HEADER>
        <CODE>200</CODE>
        <MESSAGE>GET BUILDING INFO LIST - SUCCESS</MESSAGE>
    </HEADER>
    <BODY>
        <BLDG_LIST>
            <BLDG_INFO>
                <ZIPCODE>111-222</ZIPCODE>
                <BLDG_NM>White Bread Luxury Hotel</BLDG_NM>
            </BLDG_INFO>
            <BLDG_INFO>
                <ZIPCODE>333-444</ZIPCODE>
                <BLDG_NM>Toast Bread Luxury Hotel</BLDG_NM>
            </BLDG_INFO>
        </BLDG_LIST>
    </BODY>
</RESPONSE>




2. XML 과 매칭되는 POJO 생성

저는 DTO 에 JAXB 애노테이션을 붙일 때 아래와 같은 패턴을 정해서 작성합니다.
이렇게 패턴을 고정하고 작성하면 머리가 덜 아프더군요 😋

  • Class
    • @XMLRootElement(name = {태그명})
    • @XmlAccessorType(XmlAccessType.FIELD)
    • getter, setter 생성 (저는 롬복 사용)
  • Class field
    • 일반적으로는 @XmlElement(name = {태그명})
    • 단, 필드와 매칭되는 Xml Tag 가 List 처럼 Element 를 여러 개 갖으면?
      • @XmlElementWrapper(name = {리스트 태그명})
      • @XmlElement(name = {리스트 요소 태그명})

@XmlRootElement(name = "RESPONSE")
@XmlAccessorType(XmlAccessType.FIELD)
@Data
private static class Response {
	
	@XmlElement(name = "HEADER")
	private Header header;
	
	@XmlElement(name = "BODY")
	private Body body;
}

@XmlRootElement(name = "HEADER")
@XmlAccessorType(XmlAccessType.FIELD)
@Data
private static class Header {
	
	@XmlElement(name = "CODE")
	private String code;
	
	@XmlElement(name = "MESSAGE")
	private String message;
}

@XmlRootElement(name = "BODY")
@XmlAccessorType(XmlAccessType.FIELD)
@Data
private static class Body {
	
	@XmlElementWrapper(name = "BLDG_LIST")
	@XmlElement(name = "BLDG_INFO")
	private List<BldgInfo> bldgList;
	
}

@XmlRootElement(name = "BODY")
@XmlAccessorType(XmlAccessType.FIELD)
@Data @NoArgsConstructor @AllArgsConstructor
private static class BldgInfo {
	
	@XmlElement(name = "ZIPCODE")
	private String zipcode;
	
	@XmlElement(name = "BLDG_NM")
	private String bldgNm;
	
}



3. marshalling , unmarshalling

marshalling(마셜링), unmarshalling(언마셜링) 의 의미는 다음과 같습니다.

  • marshalling : JAVA -> XML 변환
  • unmarshalling : XML -> JAVA 변환

지금부터 JAXB API 에서 제공하는 JAXBContext 를 사용해서
이러한 마셜링, 언마셜링을 직접 실습해보겠습니다.


3-1. 마셜링

@Test
@DisplayName("converting pojo to xml string or file (= marshalling)")
void convertPojoToXmlTest() {
	try {
		JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		
		Response response = new Response();
		Header header = new Header();
		Body body = new Body();
		
		List<BldgInfo> bldgInfos = List.of(
			new BldgInfo("111-222", "luxury hotel"),
			new BldgInfo("222-333", "luxury motel")
		);
		
		body.setBldgList(bldgInfos);
		
		header.setCode("200");
		header.setMessage("GET BUILDING INFO LIST - SUCCESS");
		
		response.setHeader(header);
		response.setBody(body);
		
		// The following code adds a new line to improve readability.
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		
		// read xml string from marshaller
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		marshaller.marshal(response, os);


		String xmlString = os.toString(StandardCharsets.UTF_8);
		// remark: if your jdk version is lower than 10, use code below
		//         ==> String s = new String(os.toByteArray(), StandardCharsets.UTF_8);

		log.debug("\n{}", xmlString);
        
        
        // 혹시라도 "<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"
        // 에서 standalone="yes" 를 빼고 싶으신 분들은 아래 코드를 참고해주세요.
        
		// marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
		// StringWriter stringWriter = new StringWriter(); 
		// stringWriter.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		// marshaller.marshal(response, stringWriter);
		// System.out.println(stringWriter.toString());
		
		
	} catch (JAXBException e) {
		fail(e.getMessage());
	}
	
}

마셜링 테스트 코드 실행결과:



3-2. 언마셜링

@Test
@DisplayName("converting complicating xml file to pojo (= unmarshalling)")
void complicateXmlToPojoConvertingTest() {
	try (InputStream is = this.getClass().getResourceAsStream("./complicate.xml")) {
		
		JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		
		Response buildingInfoResponse = (Response) unmarshaller.unmarshal(is);
		log.debug("header info = {}", buildingInfoResponse.getHeader());
		log.debug("body info = {}", buildingInfoResponse.getBody());
		log.debug("building list info = {}", buildingInfoResponse.getBody().getBldgList());
		
	} catch (IOException | JAXBException e) {
		fail(e.getMessage());
	}
}

마셜링 테스트 코드 실행결과:




4. Spring MVC - RestTemplate

Spring MVC 에서 제공하는 RestTemplate 을 사용하면 아주 간편하게 언마셜링이 가능합니다.
Controller Endpoint 코드를 기준으로 예시를 작성하겠다.

@GetMapping("/buldingDong")
@ResponseBody
public ResponseDTO bldgDongList(@RequestParam MultiValueMap<String, String> params) {
	URI uri = UriComponentsBuilder.fromUriString("http://example.com")
			.queryParams(params)
            .build.toUri();
	
    XmlResponseDTO responseDTO 
    	= new RestTemplate().getForObject(uri, Response.class);

	return responseDTO;
}




✨ 참고 링크


profile
백엔드를 계속 배우고 있는 개발자입니다 😊
post-custom-banner

0개의 댓글