JAXBContext, Unmarshaller와 lombok
포스트
취소

JAXBContext, Unmarshaller와 lombok

해당 원글은 미디엄 블로그에서 작성한 내용을 해당 블로그에 기록하기 위해 가져옴. 원글은 2021.01.22일 작성.

개요

  • 작업하다 보면 가끔씩 xml을 parsing해야 할 때가 종종 있다. json이면 참 편했을텐데 현실은 그렇지 않지… xml을 parsing하기 위해 어떤 libarary를 사용하면 좋을까~ 하고 검색해보면 꽤 다양한 library가 존재한다. gson-xml이라는 gson에서 만든 library도 있고, JDK 에서 제공하는 XPath, JAXBContext도 있고 다양한데 무엇을 선택하여 사용할지는 개발자의 마음이다.
  • 여기선 어떤게 좋을까?라기보다 JAXBContext 사용하다가 발견한 사소한 정보를 공유하고자 한다.

우선 기본적인 JAXBContext 사용법은 검색하면 쭈르륵 나오니까 패스. 우리(?)가 좋아하는 lombok과 같이 한번 사용해보자.

사용 예시

xml정보를 담을 간단한 class

1
2
3
4
5
6
7
8
@Getter
@XmlRootElement
public class User {
   @XmlElement
   private String name;
   @XmlElement
   private String character;
}

그리고 본격적으로 JABContext를 사용하여 xml을 parsing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void unmarshallerTest() throws Exception {
   String response = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
         + "<user>\n"
         + "    <name>토끼겅쥬</name>\n"
         + "    <character>전사</character>\n"
         + "</user>";
   JAXBContext jaxbContext = JAXBContext._newInstance_(User.class);
   Unmarshaller unmars = jaxbContext.createUnmarshaller();
   User user = (User) unmar.unmarshal(new StringReader(response));

   System.out.println( "name : " + user.getName());
   System.out.println( "character : " + user.getCharacter());
}

결과는 기대했던대로 잘 나온다.

1
2
name : 토끼겅쥬
character : 전사

여기서 User class에 임의의 새로운 값이 필요하여 Getter를 Data annotation으로 바꾸고 다시 실행해보자.

1
2
3
4
5
6
7
8
9
10
@Data
@XmlRootElement
public class User {
   @XmlElement
   private String name;
   @XmlElement
   private String character;

   private int level;
}

결과는 runtime Exception이 발생해버렸다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions클래스에 동일한 이름 "character"을(를) 사용하는 속성이 두 개 있습니다.
 this problem is related to the following location:
  at public java.lang.String com.naver.land.vapi.User.getCharacter()
  at com.naver.land.vapi.User
 this problem is related to the following location:
  at private java.lang.String com.naver.land.vapi.User.character
  at com.naver.land.vapi.User
클래스에 동일한 이름 "name"을(를) 사용하는 속성이 두 개 있습니다.
 this problem is related to the following location:
  at public java.lang.String com.naver.land.vapi.User.getName()
  at com.naver.land.vapi.User
 this problem is related to the following location:
  at private java.lang.String com.naver.land.vapi.User.name
  at com.naver.land.vapi.User
  • 이유가 뭘까나? JAXBContext, Annotation, Lombok 사용의 콜라보로 인한 오류인데 JAXBContext 동작 방식을 살펴보자

  • 우선 @XmlElement annotation에 주목할 필요가 있다. unmarshal을 할때 해당 annotation이 지정 된 곳에 값이 세팅이 되는데 위의 예제엔 필드값에 지정이 되어 있기 때문에 필드값으로 바로 값이 세팅이 된다. 그래서 setter를 사용하고 싶으면 setter 메소드에 annotation을 지정하면 된다.

이런식으로 setter에 @XmlElement를 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Getter
@XmlRootElement
public class User {
   private String name;
   private String character;

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

   @XmlElement
   public void setCharacter(String character) {
      this.character = character;
   }
}
  • 자 이쯤 되면 Data lombok annotaion을 사용했을 때 왜 아까와 같은 Exception이 발생했는지 파악했을 것이다. Data를 사용했다면 아래와 같이 풀어서 Getter,Setter가 각 필드에 지정되었을 것이다. XmlElement이 Setter에도 지정이 되어 필드,Setter 두곳에 동일한 이름의 속성이 생성되어 버린것이다.
1
2
3
4
5
6
7
8
9
10
11
@XmlRootElement
public class User {
   @Getter
   @Setter
   @XmlElement
   private String name;
   @Getter
   @Setter
   @XmlElement
   private String character;
}

마무리

  • 그러므로 JABContext를 사용할 때 lombok을 같이 사용하고 싶다면 필드별로 Getter, Setter를 지정하는 것이 좋다.
  • 넘 소박하고 사소한 정보였지만, 나 같이 오랜만에 xml parsing하려는 분이 있다면 도움이 되었길 바란다. ㅠ
  • 참고로 검색하다가 봤는데 JABContext는 한번만 생성되는 게 좋다고 한다. 리소스가 생각보다 많이 소모 된다고 함. 반복문 안보다는 바깥에 놓도록 하자.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

Java Checked Exception, Unchecked Exception

한국투자증권 모바일 API 활용하기 1탄