'Simple을 사용하여 XML 직렬화하기'에 해당되는 글 1건

  1. 2012.01.31 Simple을 사용하여 XML 직렬화하기
반응형

Simple이란?

Simple은 XML의 직렬화 및 역직렬화 프로세스를 단순화하는 데 사용되는 Java 프레임워크이다. 이름에서 알 수 있듯이 Simple을 사용하는 개발자는 POJO(plain old Java object)를 XML 문서로 변환하는 직렬화 프로세스를 단순화할 수 있다. Simple은 그 반대의 기능도 제공한다. 즉, XML 문서를 POJO로 변환할 수 있으며 이 프로세스를 역직렬화라고 한다.

Simple은 그 이름에 걸맞게 어노테이션을 사용하여 직렬화 또는 역직렬화 프로세스를 수행한다. POJO는 해당 XML 문서의 표시 방법에 따라 어노테이트된다. 일부 필드는 속성으로 어노테이트되고 다른 필드는 요소로 어노테이트된다. 클래스는 일반적으로 루트 요소로 어노테이트된다. 직렬화 프로세스 동안 어노테이션을 해석하고 해당 XML 문서를 생성하는 지루한 프로세스는 프레임워크에 의해 자동으로 처리된다. 또한 어노테이션은 XML 문서가 POJO로 변환되는 역직렬화 프로세스 동안에도 해석된다.

Simple을 사용하면 여러 가지 장점을 얻을 수 있다. 첫 번째 장점은 애플리케이션을 빠르게 개발할 수 있다는 것이다. Simple은 매우 단순하므로 과도한 학습 곡선 또는 개발 오버헤드 없이도 XML 직렬화 및 역직렬화를 사용하는 강력한 애플리케이션을 빠르게 구현할 수 있다.

Simple의 다음 장점은 구성이 필요하지 않다는 것이다. 앞에서 언급한 대로 Simple은 어노테이션을 사용한다. 이러한 어노테이션은 일반적으로 이러한 특성을 가진 프레임워크에 사용되는 XML 기반 구성 파일 대신 사용된다.

마지막으로 Simple을 사용하는 애플리케이션의 풋프린트에 최소한의 공간만 추가된다는 장점이 있다. 즉, 239KB에 불과한 JAR(Java Archive) 파일만 추가된다. 또한 Simple은 다른 프레임워크와 마찬가지로 다른 JAR 파일을 사용하지 않는다.

Simple 다운로드하기

Simple을 사용하려면 먼저 참고자료에 나열되어 있는 Simple 웹 사이트로 이동하여 아카이브를 다운로드해야 한다. 하지만 이 프로세스와 관련하여 좋은 소식과 나쁜 소식이 있다. 좋은 소식은 아카이브라 무료라는 것이고 나쁜 소식은 아카이브가 .tar.gz 형식이라는 것이다. 따라서 Microsoft® Windows® 기본 아카이브 추출기로는 이 아카이브를 열 수가 없기 때문에 WinZip 또는 유사한 아카이브 유틸리티가 필요하다.

파일을 추출한 후 jar 디렉토리를 보면 하나의 JAR 파일(simple-xml-2.1.4.jar)이 있다. 이 파일은 컴파일 시간 및 런타임에 클래스 경로에 있어야 하는 JAR 파일이다.

직렬화하기

오브젝트를 XML 문서로 직렬화하는 작업은 매우 간단하다. 다음 두 단계만 수행하면 된다.

  1. 적절한 어노테이션을 사용하여 POJO를 작성한다.
  2. 직렬화 프로세스를 실제로 수행하는 몇 줄의 코드를 작성한다.

이 기사에서는 필자의 기사에서 자주 사용되는 테마인 루어를 다시 한번 테마로 사용할 것이며 Listing 1이 바로 루어에 대한 정보를 나타내는 POJO이다.


Listing 1. Lure 클래스
@Root
public class Lure {

     @Attribute
     private String type;
	
     @Element
     private String company;
	
     @Element
     private int quantityInStock;
	
     @Element
     private String model;

     public String getType() {
		return type;
     }

     public void setType(String type) {
          this.type = type;
     }

     public String getCompany() {
          return company;
     }

     public void setCompany(String company) {
          this.company = company;
     }

     public int getQuantityInStock() {
          return quantityInStock;
     }

     public void setQuantityInStock(int quantityInStock) {
          this.quantityInStock = quantityInStock;
     }

     public String getModel() {
          return model;
     }

     public void setModel(String model) {
          this.model = model;
     }
}

이 POJO에는 복잡한 내용이 하나도 없다. 처음 봤을 때 어노테이션 부분이 익숙하지 않을 수도 있겠지만 이 또한 의도적인 것이다. Simple 프레임워크가 그 이름에 걸맞다고 한 필자의 말을 떠올려 보라.

@Root 어노테이션은 XML 문서의 루트 요소를 설명한다. 모든 XML 문서에는 루트 요소가 필요하므로 이 어노테이션을 반드시 포함시켜야 한다.

type 필드 위의 @Attribute 어노테이션은 해당 필드를 속성으로 식별한다. 이 속성은 루트 요소에 속성으로 추가된다.

마지막으로 남아 있는 @Element 어노테이션은 company, quantityInStockmodel이라는 세 필드의 바로 위에 사용된다. 이러한 필드는 XML 문서 내의 요소를 나타낸다.

POJO의 나머지 부분은 JavaBean 표준에 따라 접근자와 변경자로 구성되어 있다.

이제 POJO가 완료되었으므로 직렬화 코드를 작성할 차례이다. Listing 2에서 이 직렬화 코드를 볼 수 있다.


Listing 2. LureExample 클래스
public static void main(String[] args) {
     try {
          Serializer serializer = new Persister();
          Lure lure = new Lure();
          lure.setCompany("Donzai");
          lure.setModel("Marlin Buster");
          lure.setQuantityInStock(23);
          lure.setType("Trolling");
		
          File result = new File("lure.xml");
          serializer.write(lure, result);
     } catch (Exception e) {
          e.printStackTrace();
     }
}

이 코드에서는 가장 먼저 Persister 오브젝트를 인스턴스화한다. 이 클래스는 Simple 프레임워크의 일부이고 Serializer 인터페이스를 구현한다.

그런 다음 Lure 오브젝트를 인스턴스화하고 필드를 적절히 설정한다. 여기에서는 루어를 제조하는 회사의 이름이 Donzai이고 모델 이름이 Marlin Buster이다. 재고량은 23개이다. 마지막으로 루어의 유형은 Trolling이다.

그런 다음 XML 문서가 될 파일의 이름을 사용하여 File 오브젝트를 인스턴스화한다. 이 경우에는 파일 이름이 lure.xml이다.

마지막으로 직렬 변환기를 호출하여 파일을 작성한다. write() 메소드에 두 개의 매개변수가 제공된다. 첫 번째 매개변수는 POJO이고 두 번째 매개변수는 File 오브젝트이다.

이제 이 코드를 실행할 수 있다. Listing 2는 Java 애플리케이션이므로 자주 사용하는 IDE(Integrated Development Environment)를 사용하여 코드를 실행할 수 있다. simple-xml-2.1.4.jar이 클래스 경로에 있는지 확인한다. Eclipse를 사용하는 경우에는 파일을 마우스 오른쪽 단추로 클릭하고 Run As를 선택한 다음 표시되는 메뉴에서 Java Application을 선택하면 된다.

애플리케이션이 정상적으로 작동했다면 Listing 3과 같은 결과 XML 문서가 생성되어야 한다. (당연히 정상적으로 작동되어야 하며 정말 간단한 작업이다.)


Listing 3. LureExample 출력
<lure type="Trolling">
   <company>Donzai</company>
   <quantityInStock>23</quantityInStock>
   <model>Marlin Buster</model>
</lure>

Listing 3에서는 두 가지 사항에 주목해야 한다. 첫 번째는 루어 유형이 루트 요소의 속성이라는 것이다. 이는 @Element가 아닌 @Attribute를 사용하여 POJO의 type 필드를 어노테이트했기 때문에 완벽하게 작동한다.

결과 XML과 관련하여 또 하나 중요한 점은 요소 이름이 JavaBean 표준을 따른다는 것이다. 예를 들어, 루트 요소는 lure이고 클래스 이름은 Lure이다. 세 개의 하위 요소 이름도 해당 필드 이름과 완벽하게 일치한다. 다시 한번 말해서, 이 방법을 사용하면 루트 요소 이름과 하위 요소 이름이 서로 다른 패턴을 따르지 않아도 되기 때문에 이는 매우 실용적인 방법이다.

역직렬화하기

오브젝트를 직렬화하는 작업이 매우 쉬웠던 것처럼 역직렬화하는 방법도 매우 쉽다.

역직렬화는 XML 문서를 POJO로 변환하는 프로세스이다. 한 가지 좋은 점은 조금 전에 작성한 XML 문서를 역직렬화 작업에 사용할 수 있다는 것이다.

Listing 4에서는 역직렬화 코드를 보여 준다.


Listing 4. LureExample2 클래스
public static void main(String[] args) {
     try {
          Serializer serializer = new Persister();
          File source = new File("lure.xml");
          Lure lure = serializer.read(Lure.class, source);

          System.out.println(lure.getCompany());
          System.out.println(lure.getModel());
          System.out.println(lure.getQuantityInStock());
          System.out.println(lure.getType());
     } catch (Exception e) {
          e.printStackTrace();
     }
}

먼저 Serializer 인터페이스를 구현하는 Persister 오브젝트를 인스턴스화한다.

File 오브젝트를 인스턴스화하는 행도 익숙해 보이겠지만 이 경우에는 명확한 차이가 있다. Listing 3에서는 존재하지 않는 파일에 대한 File 오브젝트를 인스턴스화했지만 여기에서는 파일이 존재하고 있는 것으로 가정하고 있다. (실제로도 파일이 있어야 한다.)

그런 다음 Serializer 오브젝트의 read 메소드를 호출하여 POJO를 인스턴스화한다. read 메소드는 두 개의 매개변수를 사용한다. 하는 POJO 클래스이고 다른 하나는 데이터가 포함된 XML 파일을 나타내는 File 오브젝트이다.

마지막으로 모든 정보가 올바르게 읽혔는지 확인하기 위해 POJO의 모든 정보를 출력한다.

이 코드를 실행하면 Listing 5와 같은 출력이 표시되어야 한다.


Listing 5. LureExample2 출력
Donzai
Marlin Buster
23
Trolling

전체 트리 직렬화

지금까지 직렬화 및 역직렬화했던 XML 문서는 상당히 단순했다.

하지만 대부분의 XML 문서는 훨씬 더 복잡하다. 일반적으로 이러한 문서에는 중첩 요소뿐만 아니라 여러 개의 하위 요소를 가지고 있는 일대다 반복 요소가 여러 계층으로 포함되어 있다.

다행스럽게도 Simple은 그 이름에 걸맞게 복잡한 문서도 쉽게 처리할 수 있다. POJO 내에서 POJO를 중첩하는 것처럼 간단하게 처리할 수 있다. Listing 6을 살펴보자.


Listing 6. AdvancedLure 클래스
@Attribute
private String type;

@Element
private String company;
	
@Element
private int quantityInStock;
	
@Element
private String model;
	
@Element
private ConfigurationScheme configurationScheme;

Listing 6AdvancedLure 클래스는 Listing 1Lure 클래스와 매우 유사하다. 한 가지 차이점은 configurationScheme라는 또 하나의 필드가 포함되어 있다는 것이다. 구성 스키마는 Listing 7에서 설명하는 또 다른 POJO에 의해 표시된다.


Listing 7. ConfigurationScheme 클래스
@Root
public class ConfigurationScheme {
	
@Element
private String color;
	
@Element
private int size;

여기에서는 간단하게 설명하기 위해 접근자와 변경자를 생략했다.

Listing 7에서는 익숙한 어노테이션을 볼 수 있다. 이러한 어노테이션은 Listing 1에서 사용했던 것과 같은 어노테이션이다.

이제 Listing 8과 같은 결과를 얻을 수 있다.


Listing 8. LureExample3 클래스
public static void main(String[] args) {
     try {
          Serializer serializer = new Persister();
          AdvancedLure lure = new AdvancedLure();
          lure.setCompany("Donzai");
          lure.setModel("Marlin Buster");
          lure.setQuantityInStock(23);
          lure.setType("Trolling");
			
          ConfigurationScheme configurationScheme = new ConfigurationScheme();
          configurationScheme.setColor("Blue");
          configurationScheme.setSize(3);
          lure.setConfigurationScheme(configurationScheme);
			
          File result = new File("advancedlure.xml");
          serializer.write(lure, result);
     } catch (Exception e) {
          e.printStackTrace();
     }
}

Listing 2와 비교했을 때 Listing 8의 큰 차이점은 ConfigurationScheme 오브젝트를 인스턴스화한 다음 AdvancedLure 오브젝트의 해당 필드를 설정한다는 것이다.

이 코드를 실행하면 Listing 9와 같은 출력이 표시되어야 한다.


Listing 9. LureExample3 출력
<advancedLure type="Trolling">
   <company>Donzai</company>
   <quantityInStock>23</quantityInStock>
   <model>Marlin Buster</model>
   <configurationScheme>
      <color>Blue</color>
      <size>3</size>
   </configurationScheme>
</advancedLure>

이 출력을 보면 중첩 요소가 예상대로 정확히 직렬화되었다는 것을 알 수 있다.

열거

XML 문서에는 특정 요소의 열거가 자주 사용된다. 즉, 동일한 요소 이름이 하위 또는 속성 데이터만 변경된 채로 반복해서 자주 사용된다. 다행히도 Simple은 이 상황을 잘 처리할 수 있다.

이 기능을 확인하기 위해 먼저 Inventory라는 새 클래스를 작성한다. 이 클래스에는 재고품이 보관되어 있는 창고의 이름과 해당 재고 창고에서 찾을 수 있는 루어의 목록이 있다(Listing 10 참조).


Listing 10. Inventory 클래스
@Root
public class Inventory {
	
     @ElementList
     private List<AdvancedLure> lures;
	
     @Attribute
     private String warehouse;

이 Listing에서는 @ElementList라는 새 어노테이션을 사용하고 있다. 이 어노테이션은 해당 List 오브젝트가 XML 요소의 열거를 나타낸다는 의미를 지닌다.

이 POJO의 직렬화를 보여 주기 위해 사용된 코드는 더 간단한 POJO(예: Listing 2에서 직렬화했던 POJO)를 직렬화하는 데 사용했던 코드와 마찬가지로 쉽게 이해할 수 있다. Listing 11을 살펴보자.


Listing 11. LureExample4 클래스
Serializer serializer = new Persister();

AdvancedLure lure = new AdvancedLure();
lure.setCompany("Donzai");
lure.setModel("Marlin Buster");
lure.setQuantityInStock(23);
lure.setType("Trolling");
			
ConfigurationScheme configurationScheme = new ConfigurationScheme();
configurationScheme.setColor("Blue");
configurationScheme.setSize(3);
lure.setConfigurationScheme(configurationScheme);
			
			
AdvancedLure lure2 = new AdvancedLure();
lure2.setCompany("Ziggi");
lure2.setModel("Tuna Finder");
lure2.setQuantityInStock(44);
lure2.setType("Trolling");
		
ConfigurationScheme configurationScheme2 = new ConfigurationScheme();
configurationScheme2.setColor("Red");
configurationScheme2.setSize(5);
lure2.setConfigurationScheme(configurationScheme2);
			
List<AdvancedLure> lures = new ArrayList<AdvancedLure>();
lures.add(lure);
lures.add(lure2);
			
Inventory inventory = new Inventory();
inventory.setLures(lures);
inventory.setWarehouse("Midwest");
			
File result = new File("inventory.xml");

serializer.write(inventory, result);

이 코드를 실행하면 inventory.xml이라는 XML 파일이 작성된다. 이 파일의 내용은 Listing 12와 같다.


Listing 12. LureExample4 출력
<inventory warehouse="Midwest">
   <lures>
      <advancedLure type="Trolling">
         <company>Donzai</company>
         <quantityInStock>23</quantityInStock>
         <model>Marlin Buster</model>
         <configurationScheme>
            <color>Blue</color>
            <size>3</size>
         </configurationScheme>
      </advancedLure>
      <advancedLure type="Trolling">
         <company>Ziggi</company>
         <quantityInStock>44</quantityInStock>
         <model>Tuna Finder</model>
         <configurationScheme>
            <color>Red</color>
            <size>5</size>
         </configurationScheme>
      </advancedLure>
   </lures>
</inventory>

이 Listing을 보면 Listing 11에서 인스턴스화하고 작성했던 POJO와 출력이 완전히 같다는 것을 알 수 있다. 두 개의 advancedLure 요소가 있고 각 요소에는 해당 POJO를 채우는 데 사용했던 데이터가 있다. 여기에서도 중첩이 적절히 사용되고 있다.

생성자

코드에서 변경되지 않는 POJO를 사용할 수 있다. 이 경우 setter 메소드를 사용하여 필드 속성을 수정하는 대신 생성자를 사용하여 이러한 값을 설정할 수 있다. Simple은 이 상황을 잘 처리할 수 있다.

이 경우에는 필드 이름 위가 아닌 생성자 매개변수 내에서 어노테이션을 지정한다. 해당 접근자 메소드 위에도 어노테이션이 필요하다. Listing 13을 살펴보자.


Listing 13. Inventory2 클래스
public Inventory2(@ElementList(name="lures") List<AdvancedLure> lures,
 @Attribute(name="warehouse") String warehouse) {
     this.lures = lures;
     this.warehouse = warehouse;
}

@ElementList(name="lures")
public List<AdvancedLure> getLures() {
     return lures;
}

@Attribute(name="warehouse")
public String getWarehouse() {
     return warehouse;
}

어노테이션을 생성자 매개변수의 일부로 지정할 경우에는 매개변수에 해당하는 필드 이름도 지정해야 한다. 첫 번째 매개변수에 대한 필드 이름은 lures이고 두 번째 매개변수에 대한 필드 이름은 warehouse이다.

그런 다음 접근자 메소드 위에 일치하는 어노테이션 및 필드 이름을 제공한다. lures에 대한 getter 바로 위에서 생성자에 사용된 어노테이션과 완전히 일치하는 해당 어노테이션을 볼 수 있다. 이와 마찬가지로 warehouse에 대한 getter 위의 어노테이션도 해당 어노테이션과 완전히 일치한다.

이 코드를 실행하기 위해 먼저 Listing 11을 약간 수정하여 Listing 14의 코드를 포함시킨다.


Listing 14. LureExample5 클래스
Inventory2 inventory = new Inventory2(lures,"MidWest");

여기에서는 Listing 11에서 사용했던 setter 메소드 대신 Listing 14의 코드를 사용하며 나머지 부분은 모두 동일하다.

템플리트 필터

XML 문서를 역직렬화할 때 템플리트 필터 또는 기본값을 사용할 수 있다는 점은 Simple의 또 다른 주요 기능이다.

이제 원래의 lure.xml로 돌아가서 Listing 15처럼 약간만 수정해 보자.


Listing 15. Lure2.xml 문서
<lure type="Trolling">
   <company>${lure.company}</company>
   <quantityInStock>23</quantityInStock>
   <model>Marlin Buster</model>
</lure>

이 문서의 company 요소에는 실제 회사의 이름 대신 Apache Ant 유형의 변수 선언이 포함되어 있으며 변수의 이름은 lure.company이다.

템플리트 필터를 사용하면 런타임에 변수 선언을 실제 값으로 대체하여 설정할 수 있다. 이를 수행하는 쉬운 방법으로는 변수 이름과 해당 값을 연결하는 Map 오브젝트를 사용하는 것이다. 그런 다음 Map 오브젝트를 사용하여 Simple 프레임워크에서 Filter 오브젝트를 생성한다. 그런 다음 Filter 오브젝트를 사용하여 Persister 오브젝트를 생성한다. Listing 16을 살펴보자.


Listing 16. LureExample6 클래스
Map<String,String> map = new HashMap<String,String>();
map.put("lure.company", "Monmouth");
Filter filter = new MapFilter(map);
			
Serializer serializer = new Persister(filter);
File source = new File("lure2.xml");

Lure lure = serializer.read(Lure.class, source);

System.out.println(lure.getCompany());
System.out.println(lure.getModel());
System.out.println(lure.getQuantityInStock());
System.out.println(lure.getType());

Listing 16Listing 2와 크게 다르지 않다. 주요 차이점은 String 값을 String 키에 연결하는 HashMap이 인스턴스화된다는 것이다. 이 경우 String 키는 예상대로 Listing 15에서 사용했던 변수 이름인 lure.company이다. 여기에서는 앞에서 보았던 XML 문서에 있던 값인 Donzai대신 Monmouth라는 값을 이 키에 지정한다.

이 코드를 실행하면 Listing 17과 같은 출력이 표시되어야 한다.


Listing 17. LureExample6 출력
Monmouth
Marlin Buster
23
Trolling

이 출력을 보면 템플리트 필터가 변수 이름 lure.companyMap 오브젝트에 있는 해당 키 이름에 연결된 실제 값으로 대체했다는 것을 알 수 있다. 이 출력은 예상과 정확히 일치한다.

결론

지금까지 살펴보았듯이 Simple은 강력한 XML 직렬화 및 역직렬화 프레임워크이며 사용하기도 쉽고, 구성도 필요하지 않기 때문에 빠른 개발이 가능하다.

Simple에는 여러 가지 강력한 기능이 있지만 여기에서는 지면의 제약으로 인해 일부 기능만을 살펴보았다. 소프트웨어 개발 작업 중에 XML 직렬화 및 역직렬화가 필요한 개발자라면 Simple을 사용해 보기를 적극 권장한다.



다운로드 하십시오

설명 이름 크기 다운로드 방식
Source code for the examples used in this article Simple.zip 5KB HTTP

다운로드 방식에 대한 정보

Posted by 1010