1. 개요
이 사용방법(예제)에서는 XStream 라이브러리를 사용하여 Java 개체를 XML로 직렬화하는 방법을 배웁니다.
2. 특징
XStream을 사용하여 XML을 직렬화 및 역직렬화하면 몇 가지 흥미로운 이점이 있습니다.
- 올바르게 구성되면 매우 깨끗한 XML 을 생성합니다.
- XML 출력 을 사용자 정의 할 수 있는 중요한 기회를 제공합니다 .
- 순환 참조를 포함한 객체 그래프 지원
- 대부분의 사용 사례에서 XStream 인스턴스는 일단 구성되면 스레드로부터 안전합니다 (어노테이션 사용 시 주의 사항이 있음).
- 문제를 진단하는 데 도움이 되도록 예외 처리 중에 명확한 메시지가 제공됩니다 .
- 버전 1.4.7부터 특정 유형의 직렬화를 허용하지 않는 Security 기능 이 있습니다.
3. 프로젝트 설정
프로젝트에서 XStream을 사용하기 위해 다음 Maven 의존성을 추가합니다.
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.18</version>
</dependency>
4. 기본 사용법
XStream 클래스는 API의 파사드입니다 . XStream 인스턴스를 만들 때 스레드 안전 문제도 처리해야 합니다.
XStream xstream = new XStream();
인스턴스가 생성되고 구성되면 어노테이션 처리를 활성화하지 않는 한 마샬링/언마샬링을 위해 여러 스레드에서 인스턴스를 공유할 수 있습니다.
4.1. 드라이버
DomDriver , StaxDriver , XppDriver 등과 같은 여러 드라이버가 지원됩니다 . 이러한 드라이버는 성능 및 리소스 사용 특성이 다릅니다.
기본적으로 XPP3 드라이버가 사용되지만 물론 드라이버를 쉽게 변경할 수 있습니다.
XStream xstream = new XStream(new StaxDriver());
4.2. XML 생성
고객 을 위한 간단한 POJO를 정의하는 것으로 시작하겠습니다 .
public class Customer {
private String firstName;
private String lastName;
private Date dob;
// standard constructor, setters, and getters
}
이제 개체의 XML 표현을 생성해 보겠습니다.
Customer customer = new Customer("John", "Doe", new Date());
String dataXml = xstream.toXML(customer);
기본 설정을 사용하면 다음 출력이 생성됩니다.
<com.baeldung.pojo.Customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</com.baeldung.pojo.Customer>
이 출력에서 포함하는 태그 가 기본적 으로 Customer 의 정규화된 클래스 이름을 사용한다는 것을 분명히 알 수 있습니다 .
기본 동작이 우리의 필요에 맞지 않는다고 결정할 수 있는 많은 이유가 있습니다. 예를 들어 애플리케이션의 패키지 구조를 노출하는 것이 불편할 수 있습니다. 또한 생성된 XML은 훨씬 더 깁니다.
5. 별칭
별칭 은 기본 이름을 사용 하는 대신 요소에 사용하려는 이름입니다.
예를 들어 Customer 클래스 에 대한 별칭을 등록하여 com.baeldung.pojo.Customer 를 고객 으로 바꿀 수 있습니다. 클래스 속성에 별칭을 추가할 수도 있습니다. 별칭을 사용하여 XML 출력을 훨씬 더 읽기 쉽게 만들고 Java 관련성을 줄일 수 있습니다.
5.1. 클래스 별칭
별칭은 프로그래밍 방식으로 또는 어노테이션을 사용하여 등록할 수 있습니다.
이제 @XStreamAlias 로 Customer 클래스에 어노테이션을 달아 보겠습니다 .
@XStreamAlias("customer")
이제 이 어노테이션을 사용하도록 인스턴스를 구성해야 합니다.
xstream.processAnnotations(Customer.class);
또는 프로그래밍 방식으로 별칭을 구성하려는 경우 아래 코드를 사용할 수 있습니다.
xstream.alias("customer", Customer.class);
별칭을 사용하든 프로그래밍 방식 구성을 사용하든 Customer 개체의 출력은 훨씬 깔끔합니다.
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>
5.2. 필드 별칭
별칭 클래스에 사용된 것과 동일한 어노테이션을 사용하여 필드에 대한 별칭을 추가할 수도 있습니다. 예를 들어 XML 표현에서 firstName 필드를 fn 으로 바꾸 려면 다음 어노테이션을 사용할 수 있습니다.
@XStreamAlias("fn")
private String firstName;
또는 프로그래밍 방식으로 동일한 목표를 달성할 수 있습니다.
xstream.aliasField("fn", Customer.class, "firstName");
aliasField 메서드는 사용하려는 별칭, 속성이 정의된 클래스 및 별칭을 지정하려는 속성 이름의 세 가지 인수를 허용합니다 .
어떤 방법을 사용하든 결과는 동일합니다.
<customer>
<fn>John</fn>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>
5.3. 기본 별칭
클래스에 대해 미리 등록된 여러 별칭이 있습니다. 다음은 그 중 몇 가지입니다.
alias("float", Float.class);
alias("date", Date.class);
alias("gregorian-calendar", Calendar.class);
alias("url", URL.class);
alias("list", List.class);
alias("locale", Locale.class);
alias("currency", Currency.class);
6. 컬렉션
이제 Customer 클래스 내에 ContactDetails List을 추가합니다.
private List<ContactDetails> contactDetailsList;
컬렉션 처리에 대한 기본 설정을 사용하면 다음과 같이 출력됩니다.
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 04:14:05.874 UTC</dob>
<contactDetailsList>
<ContactDetails>
<mobile>6673543265</mobile>
<landline>0124-2460311</landline>
</ContactDetails>
<ContactDetails>
<mobile>4676543565</mobile>
<landline>0120-223312</landline>
</ContactDetails>
</contactDetailsList>
</customer>
contactDetailsList 부모 태그 를 생략해야 하고 각 ContactDetails 요소가 고객 요소 의 자식 이기를 원한다고 가정해 보겠습니다 . 예제를 다시 수정해 보겠습니다.
xstream.addImplicitCollection(Customer.class, "contactDetailsList");
Now, when the XML is generated, the root tags are omitted, resulting in the XML below:
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 04:14:20.541 UTC</dob>
<ContactDetails>
<mobile>6673543265</mobile>
<landline>0124-2460311</landline>
</ContactDetails>
<ContactDetails>
<mobile>4676543565</mobile>
<landline>0120-223312</landline>
</ContactDetails>
</customer>
어노테이션을 사용하여 동일한 작업을 수행할 수도 있습니다.
@XStreamImplicit
private List<ContactDetails> contactDetailsList;
7. 변환기
XStream은 각각 고유한 변환 전략이 있는 Converter 인스턴스의 맵을 사용합니다. 이들은 제공된 데이터를 XML의 특정 형식으로 변환하고 다시 그 반대로 변환합니다.
기본 변환기를 사용하는 것 외에도 기본값을 수정하거나 사용자 지정 변환기를 등록할 수 있습니다.
7.1. 기존 변환기 수정
기본 설정을 사용하여 dob 태그가 생성된 방식이 만족스럽지 않다고 가정합니다 . XStream( DateConverter ) 에서 제공하는 날짜 에 대한 사용자 지정 변환기를 수정할 수 있습니다 .
xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));
위의 내용은 " dd-MM-yyyy " 형식 의 출력을 생성 합니다.
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>14-02-1986</dob>
</customer>
7.2. Custom형 변환기
이전 섹션에서와 동일한 출력을 수행하기 위해 사용자 지정 변환기를 만들 수도 있습니다.
public class MyDateConverter implements Converter {
private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
@Override
public boolean canConvert(Class clazz) {
return Date.class.isAssignableFrom(clazz);
}
@Override
public void marshal(
Object value, HierarchicalStreamWriter writer, MarshallingContext arg2) {
Date date = (Date)value;
writer.setValue(formatter.format(date));
}
// other methods
}
마지막으로 MyDateConverter 클래스를 아래와 같이 등록합니다.
xstream.registerConverter(new MyDateConverter());
개체를 문자열로 변환하도록 설계된 SingleValueConverter 인터페이스 를 구현하는 변환기를 만들 수도 있습니다 .
public class MySingleValueConverter implements SingleValueConverter {
@Override
public boolean canConvert(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}
@Override
public String toString(Object obj) {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
Date date = ((Customer) obj).getDob();
return ((Customer) obj).getFirstName() + ","
+ ((Customer) obj).getLastName() + ","
+ formatter.format(date);
}
// other methods
}
마지막으로 MySingleValueConverter 를 등록합니다 .
xstream.registerConverter(new MySingleValueConverter());
MySingleValueConverter 를 사용 하면 고객 에 대한 XML 출력 은 다음과 같습니다.
<customer>John,Doe,14-02-1986</customer>
7.3. 컨버터 우선순위
Converter 개체를 등록할 때 우선 순위 수준도 설정할 수 있습니다.
XStream javadocs 에서 :
변환기는 명시적 우선 순위로 등록할 수 있습니다. 기본적으로 이들은 XStream.PRIORITY_NORMAL로 등록됩니다. 우선 순위가 같은 변환기는 등록된 역순으로 사용됩니다. 기본 변환기, 즉 다른 등록된 변환기가 적합하지 않은 경우 사용될 변환기는 우선 순위 XStream.PRIORITY_VERY_LOW로 등록할 수 있습니다. XStream은 기본적으로 ReflectionConverter를 대체 변환기로 사용합니다.
API는 명명된 여러 우선 순위 값을 제공합니다.
private static final int PRIORITY_NORMAL = 0;
private static final int PRIORITY_LOW = -10;
private static final int PRIORITY_VERY_LOW = -20;
8. 필드 생략
어노테이션이나 프로그램 구성을 사용하여 생성된 XML에서 필드를 생략할 수 있습니다. 어노테이션을 사용하여 필드를 생략하려면 해당 필드에 @XStreamOmitField 어노테이션을 적용하기만 하면 됩니다.
@XStreamOmitField
private String firstName;
프로그래밍 방식으로 필드를 생략하기 위해 다음 방법을 사용합니다.
xstream.omitField(Customer.class, "firstName");
어떤 방법을 선택하든 결과는 동일합니다.
<customer>
<lastName>Doe</lastName>
<dob>14-02-1986</dob>
</customer>
9. 속성 필드
때로는 필드를 요소 자체가 아닌 요소의 속성으로 직렬화하고자 할 수 있습니다. contactType 필드 를 추가한다고 가정 합니다.
private String contactType;
contactType 을 XML 속성으로 설정 하려면 @XStreamAsAttribute 어노테이션을 사용할 수 있습니다.
@XStreamAsAttribute
private String contactType;
또는 프로그래밍 방식으로 동일한 목표를 달성할 수 있습니다.
xstream.useAttributeFor(ContactDetails.class, "contactType");
위 방법 중 하나의 출력은 동일합니다.
<ContactDetails contactType="Office">
<mobile>6673543265</mobile>
<landline>0124-2460311</landline>
</ContactDetails>
10. 동시성
XStream의 처리 모델에는 몇 가지 문제가 있습니다. 인스턴스가 구성되면 스레드로부터 안전합니다.
어노테이션 처리는 마샬링/언마샬링 직전에 구성을 수정한다는 점에 유의해야 합니다. 따라서 어노테이션을 사용하여 인스턴스를 즉석에서 구성해야 하는 경우 일반적으로 각 스레드에 대해 별도의 XStream 인스턴스 를 사용하는 것이 좋습니다 .
11. 결론
이 기사에서는 XStream을 사용하여 개체를 XML로 변환하는 기본 사항을 다루었습니다. 또한 XML 출력이 요구 사항을 충족하는지 확인하는 데 사용할 수 있는 사용자 지정에 대해서도 배웠습니다. 마지막으로 어노테이션과 관련된 스레드 안전성 문제를 살펴보았습니다.
이 시리즈의 다음 기사에서는 XML을 Java 객체로 다시 변환하는 방법에 대해 알아봅니다.
이 기사의 전체 소스 코드는 연결된 GitHub 리포지토리 에서 다운로드할 수 있습니다 .