Xstream Custom XStream Converter

황제낙엽 2011.04.26 15:07 조회 수 : 733

site_link3 http://1 

XStream is a great utility for serializing objects to XML ? and then back again.

The default XML output is neat enough ? albeit a little verbose (using fully qualified class names for example). There are plenty of tutorials available showing how to use XStream aliases to clean up the output (e.g. Manual Tweaking Output).

However, sometimes it is not enough to just tweak and alias ? more serious manipulation of the output stream is required. For example, complicated Collection-based objects come out very cumbersome (but generic ? which is the point). Luckily, XStream provides a powerful interface for custom generation (A Converter in XStream-speak).

To fully control how an object is serialised, create a new class which implements the Converter interface:

01 import com.thoughtworks.xstream.converters.Converter;
02 import com.thoughtworks.xstream.converters.MarshallingContext;
03 import com.thoughtworks.xstream.converters.UnmarshallingContext;
04 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
05 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
06   
07 public class MyCustomMapConverter implements Converter {
08   
09     @Override
10     public void marshal(Object source, HierarchicalStreamWriter writer,
11             MarshallingContext context) {
12           // TODO
13     }
14   
15     @Override
16     public boolean canConvert(Class clazz) {
17         return clazz.equals(ClassToBeConverted.class);
18     }
19   
20         @Override
21     public Object unmarshal(HierarchicalStreamReader reader,
22             UnmarshallingContext context) {
23           // TODO
24     }
25   
26 }

As can be seen above, the canConvert method tells XStream which objects should use this converter. The other two methods are called when converting objects to XML (marshal) or converting XML to objects (unmarshal).

Marshalling the object is achieved by starting and ending Nodes, while populating their attributes and values. So for example:

1 @Override
2 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
3   for (RouterConfigRuleMap routerConfigRuleMap : routerConfigRuleMapSet.getRouterConfigRuleMapSet())
4   {
5     writer.startNode("routetype");
6     writer.addAttribute("type", routerConfigRuleMap.getRouteType().toString());
7     writer.endNode();
8 }

will iterate through a collection generating the following output:

1 <routetype type="example" />
2 <routetype type="foo" />

These patterns can become arbitrarily complex.

Unmarshalling is a little more complicated. XStream will work out which (registered ? see later) Converter to use by matching the node to the class (in my head these seems like an extremely difficult task… I’m not sure how XStream has implemented this ? maybe I should look into this!) The node (and all children) will be passed to the unmarshal method.

It is up to us to define how the target object is populated from these nodes. Methods are provided to:

  • Check if the node has children (reader.hasMoreChildren())
  • Move down to the child level (reader.moveDown())
  • Move back up to the parent level (reader.moveUp())

The moveUp/Down methods can be called as many times as necessary to walk the tree.

I provide a full example as I couldn’t find many good ones on the net:

01 @Override
02 public Object unmarshal(HierarchicalStreamReader reader,    UnmarshallingContext context) {
03   
04   RouterConfigRuleMapSet routerConfigRuleMapSet = new RouterConfigRuleMapSet();
05   
06   while (reader.hasMoreChildren()) {
07     reader.moveDown();
08   
09     RouterConfigRuleMap routerConfigRuleMap = new RouterConfigRuleMap();
10     routerConfigRuleMap.setRouteType(RouteType.valueOf(reader.getAttribute("type")));
11   
12     while (reader.hasMoreChildren()) {
13         reader.moveDown();
14         RouterConfigRule routerConfigRule = new RouterConfigRule();
15   
16         if (reader.getAttribute("extractor") != null) {
17             routerConfigRule.setExtractor(reader.getAttribute("extractor"));
18         }
19   
20         if (reader.getAttribute("formatter") != null) {
21             routerConfigRule.setFormatter(reader.getAttribute("formatter"));
22         }
23   
24         if (reader.getAttribute("distributor") != null) {
25             routerConfigRule.setDistributor(reader.getAttribute("distributor"));
26         }
27   
28         routerConfigRuleMap.putRouterConfigRule(reader.getAttribute("name"), routerConfigRule);
29   
30         reader.moveUp();
31         }
32     reader.moveUp();
33     routerConfigRuleMapSet.addRouterConfigRuleMapSet(routerConfigRuleMap);
34     }
35   return routerConfigRuleMapSet;
36 }

Out of completeness, here is the equivalent marshal implementation:

01 @Override
02 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
03   
04   RouterConfigRuleMapSet routerConfigRuleMapSet = (RouterConfigRuleMapSet)source;
05   for (RouterConfigRuleMap routerConfigRuleMap : routerConfigRuleMapSet.getRouterConfigRuleMapSet())
06   {
07     writer.startNode("routetype");
08     writer.addAttribute("type", routerConfigRuleMap.getRouteType().toString());
09   
10     for (Object routerConfig : routerConfigRuleMap.routerConfigRuleMap.entrySet()) {
11         Entry<String, RouterConfigRule> entry = (Entry<String, RouterConfigRule>) routerConfig;
12         RouterConfigRule routerConfigRule = entry.getValue(); 
13   
14         writer.startNode("routename");
15         writer.addAttribute("name", entry.getKey().toString());
16         if (routerConfigRule.getExtractor() != null) {
17             writer.addAttribute("extractor", routerConfigRule.getExtractor());
18         }
19   
20         if (routerConfigRule.getFormatter() != null) {
21             writer.addAttribute("formatter", routerConfigRule.getFormatter());
22         }
23   
24         if (routerConfigRule.getDistributor() != null) {
25             writer.addAttribute("distributor", routerConfigRule.getDistributor());
26         }
27   
28         writer.endNode();
29     }
30     writer.endNode();
31   }
32 }

To register the Converter simply add the following line when you instantiate the XStream object:

1 XStream stream = new XStream();
2   stream.registerConverter(new RouterConfigMapConverter()); // NEW LINE
번호 제목 글쓴이 날짜 조회 수
50 Hello React 황제낙엽 2025.04.04 479
49 Bootstrap Icons 황제낙엽 2024.10.13 611
48 navbars 예제 황제낙엽 2024.07.15 589
47 Chatbot 용 디자인 템플릿 (ChatGPT) file 황제낙엽 2024.01.28 637
46 (Bing) Mustache 의 특징 황제낙엽 2023.12.21 587
45 문자열의 셀렉션 동작 구현 (<p>, <input>) 황제낙엽 2023.10.02 536
44 Bootstrap 5 Gallery Examples & Freebies 황제낙엽 2023.09.26 696
43 Small Business Template (Start Bootstrap) 황제낙엽 2023.09.25 626
42 Card Component 에 background image 출력하기 황제낙엽 2023.09.08 543
41 Bootstrap Gallery 를 상품 전시용으로 활용하기 위한 분석 황제낙엽 2023.09.06 578
40 dom element 에 collapse 적용하기 file 황제낙엽 2023.08.16 562
39 <i> 태그의 아이콘 종류 찾기 (fontawesome, icon) file 황제낙엽 2023.07.09 554
38 Vanilla JS Datepicker 에서 locales 지정 (한국어로 출력) 황제낙엽 2023.06.19 773
37 Feather-icons (svg) 황제낙엽 2023.06.10 532
36 프레임셋처럼 외부페이지를 동적으로 로드하기 (from bing) 황제낙엽 2023.05.24 578
35 Modal 컴포넌트를 스크립트로 감추고 보이기 황제낙엽 2023.05.22 574
34 bootstrap free date-picker 종류 (from bing) 황제낙엽 2023.05.20 566
33 라디오 버튼을 선택 해제하는 샘플 코드 (from bing) 황제낙엽 2023.05.16 558
32 radio group 에서 아이템을 선택한 값 가져오기 황제낙엽 2023.05.15 531
31 부트스트랩(Bootstrap) 테마(Theme), 템플릿(Template) 무료 사이트 file 황제낙엽 2023.04.05 562