XStream #

  • 홈페이지 : http://xstream.codehaus.org/index.html
  • XStream이란 : 객체를 XML로 변환하거나 XML을 객체로 변환하기 위한 간단한 라이브러리
  • 특징
    • Ease of use
    • No mappings required
    • Performance.
    • Clean XML.
    • Requires no modifications to objects.
    • Full object graph support.
    • Integrates with other XML APIs.
    • Customizable conversion strategies.
    • Error messages.
    • Alternative output format.

Object To XML#

  • XStream 을 사용하지 않는 경우

public String personToXML(Person person) {
  StringBuffer xml = new StringBuffer("");

  xml.append("<person>\n");
  xml.append(" <firstname>" + person.getFirstname() "</firstname>\n");
  xml.append("  <lastname><![CDATA[" + person.getLastname() "]]></lastname>\n");
  xml.append("  <phone>\n");
  xml.append("    <code>" + person.getPhone().getCode() "</code>\n");
  xml.append("    <number><![CDATA[" + person.getPhone().getNumber() "]]></number>\n");
  xml.append("  </phone>\n");
  List<Friend> friendList = person.getFriendList();
  for (Friend friend : friendList) {
    xml.append("  <friend>\n");
    xml.append("    <name><![CDATA[" + friend.getName() "]]></name>\n");
    xml.append("    <age>" + friend.getAge() "</age>\n");
    xml.append("  </friend>\n");
  }
  xml.append("</person>");

  return xml.toString();
}

개발방법마다 다르겠지만 xstream과 같은 매퍼를 사용하지 않으면 위처럼 클래스내 변수들을 로직으로 처리해서 xml을 만들어줘야 한다.

  • XStream 을 사용하는 경우

public String personToXML(Person person) {
  String xml = "";
  xstream = new XStream(new LocalXppDriver());

  xstream.alias("person", Person.class);
  xstream.alias("friend", Friend.class);
  xstream.addImplicitCollection(Person.class, "friendList", Friend.class);
  xml = xstream.toXML(person);

  return xml;
}

alias와 암시적인 collection객체 정보만 설정하면 같은 xml이 반환된다.

XML To Object#

  • XStream 을 사용하지 않는 경우

public Person xmlToPerson(String xml) {
  Person person = new Person();

  StringReader reader = new StringReader(xml);
  InputSource source = new InputSource(reader);
  Document document = null;
  try {
    document = builder.parse(source);
  catch (SAXException e) {
    e.printStackTrace();
  catch (IOException e) {
    e.printStackTrace();
  }

  PhoneNumber phoneNumber = null;
  Friend friend = null;
  List<Friend> friendList = new ArrayList<Friend>();
  
  Node rootNode = document.getFirstChild();
  NodeList nodeList = rootNode.getChildNodes();
  NodeList subNodeList = null;
  Node node = null;
  Node subNode = null;
  
  int nodeListLength = nodeList.getLength();
  int subNodeListLength = 0;
  String nodeName = "";
  for (int i = 0; i < nodeListLength; i++) {
    node = nodeList.item(i);
    nodeName = node.getNodeName();
    if (nodeName.equals("firstname")) {
      person.setFirstname(node.getChildNodes().item(0).getNodeValue());
    else if (nodeName.equals("lastname")) {
      person.setLastname(node.getChildNodes().item(0).getNodeValue());
    else if (nodeName.equals("phone")) {
      subNodeList = node.getChildNodes();
      subNodeListLength = subNodeList.getLength();
      
      phoneNumber = new PhoneNumber();
      for (int j = 0; j < subNodeListLength; j++) {
        subNode = subNodeList.item(j);
        ifj==){
          phoneNumber.setCode(Integer.parseInt(subNode.getChildNodes().item(0).getNodeValue()));
        }else ifj==){
          phoneNumber.setNumber(subNode.getChildNodes().item(0).getNodeValue());
        }
      }
      person.setPhone(phoneNumber);
    else if (nodeName.equals("friend")) {
      subNodeList = node.getChildNodes();
      subNodeListLength = subNodeList.getLength();
      
      friend = new Friend();        
      for (int j = 0; j < subNodeListLength; j++) {
        subNode = subNodeList.item(j);
        ifj==){
          friend.setName(subNode.getChildNodes().item(0).getNodeValue());
        }else ifj==){
          friend.setAge(Integer.parseInt(subNode.getChildNodes().item(0).getNodeValue()));
        }
      }
      friendList.add(friend);
    }
    person.setFriendList(friendList);
  }

  return person;
}

위 소스는 DOM을 사용하여 xml를 파싱한 후 Person객체를 만드는 과정이다. 일부 내용을 중략해서 그렇지 전체 소스를 보자면 두배 정도 된다.

  • XStream 을 사용하는 경우

public Person xmlToPerson(String xml) {
  Person person = null;
  xstream = new XStream(new LocalXppDriver());

  xstream.alias("person", Person.class);
  xstream.alias("friend", Friend.class);
  xstream.addImplicitCollection(Person.class, "friendList", Friend.class);
  person = (Personxstream.fromXML(xml);

  return person;
}

이 역시 alias와 암시적인 collection객체 설정만 해주면 된다.

XML To Object - 애노테이션 사용#

  • Model 객체에 애노테이션 사용

@XStreamAlias("person")
public class Person {
    @XStreamOmitField
    private String firstname = null;
    private String lastname = null;
    private PhoneNumber phone = null;
    @XStreamImplicit(itemFieldName = "friend")
    private List<Friend> friendList = null;

애노테이션을 사용하여 처리할수 있는 부분은 별칭, xml표현에서 생략하기, 암시적인 collection객체, 추가적인 변환자 등이다.

  • XStream을 사용하는 소스

public String personToXML(Person person) {
  String xml = "";
  xstream = new XStream(new LocalXppDriver());

  Class[] types = Person.class, Friend.class, PhoneNumber.class };
  xstream.processAnnotations(types);
  xml = xstream.toXML(person);

  return xml;
}

애노테이션으로 alias및 collection객체에 관련된 설정을 했기 때문에 애노테이션 처리를 해야 하는 클래스를 명시적으로 선언하는 부분만 추가되고 끝난다.

애노테이션#

  • 사용가능한 애노테이션
    • XStreamAlias : 클래스및 변수명에 대한 별칭, 클래스의 경우 기본은 패키지 경로를 포함한 클래스명을 표기하기 때문에 일반적으로 단축형의 별칭 사용이 필요
    • XStreamAsAttribute : 클래스및 변수를 xml요소가 아닌 xml속성으로 사용
    • XStreamConverter : 클래스및 변수를 xml요소로 바꿀때 추가적인 로직을 적용할수 있는 변환자 명시
    • XStreamConverters : 변환자를 복수로 명시
    • XStreamImplicit : collection객체를 암시적으로 명시, xml 표현에서는 제외되도록 처리
    • XStreamOmitField : xml표현에서 뺌
  • deprecated된 애노테이션
    • XStreamContainedType
    • XStreamImplicitCollection

변환자(Converter)#

  • Converter 인터페이스를 구현해야 함
  • 샘플

public class PhoneConverter implements Converter {

  public void marshal(Object arg0, HierarchicalStreamWriter arg1, MarshallingContext arg2) {
    PhoneNumber phoneNumber = (PhoneNumberarg0;
    arg1.startNode("code");
    arg1.setValue(phoneNumber.getCode() "");
    arg1.endNode();
    arg1.startNode("number");
    arg1.setValue(phoneNumber.getNumber() "");
    arg1.endNode();
  }

  public Object unmarshal(HierarchicalStreamReader arg0, UnmarshallingContext arg1) {
    PhoneNumber phoneNumber = new PhoneNumber();
    ifarg0.hasMoreChildren() ){
      arg0.moveDown();
      if ("code".equals(arg0.getNodeName().trim())) {
        phoneNumber.setCode(Integer.parseInt(arg0.getValue()));
      else if ("number".equals(arg0.getNodeName().trim())) {
        phoneNumber.setNumber("converted : "+arg0.getValue());
      }
      arg0.moveUp();
    }
    return phoneNumber;
  }

  @SuppressWarnings("unchecked")
  public boolean canConvert(Class arg0) {
    return true;
  }

}

  • 변환자 등록
    • registerConverter() 메소드 사용(전체적용)
    • registerLocalConverter() 메소드 사용(일부 클래스및 변수에만 적용)
    • @XStreamConverter(value = PhoneConverter.class) 애노테이션 사용

XSLT를 이용한 XML포맷 변환#

  • 샘플 XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" omit-xml-declaration="yes" indent="no"/>
  <xsl:template match="/cat">
    <xsl:copy>
      <xsl:apply-templates select="mName"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

  • 샘플 소스

XStream xstream = new XStream();
xstream.alias("cat", Cat.class);

TraxSource traxSource = new TraxSource(new Cat(4"Garfield"), xstream);
Writer buffer = new StringWriter();
Transformer transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new StringReader(XSLT)));
transformer.transform(traxSource, new StreamResult(buffer));

  • 결과물

<cat>
  <mName>Garfield</mName>
</cat>

XStream 확장하기#

  • 확장 케이스
    • CDATA 처리를 위한 기능이 없음.
    • PrettyPrintWriter 클래스를 확장하는 방법으로 구현.
    • writeText() 메소드 부분 구현
  • 소스
    • LocalPrettyPrintWriter.java

package openframework.study.mapper.xstream.ext;

import java.io.Writer;
import java.util.regex.Pattern;

import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;

/**
 * XStream(http://xstream.codehaus.org/)을 사용하는 확장 PrettyPrintWriter클래스
 
 @since 2008.05.30
 */
public class LocalPrettyPrintWriter extends PrettyPrintWriter {
  private static final char[] AMP = "&amp;".toCharArray();
  private static final char[] LT = "<".toCharArray();
  private static final char[] GT = ">".toCharArray();
  private static final char[] SLASH_R = " ".toCharArray();
  private static final char[] QUOT = "&quot;".toCharArray();
  private static final char[] APOS = "&apos;".toCharArray();
  
  public LocalPrettyPrintWriter(Writer writer) {
    super(writer);
  }
  
  /**
   * 실제 text값을 xml태그를 통해 표현하는 메소드이다. 
   * 상위 클래스인 PrettyPrintWriter의 다른 기능을 그대로 사용하되 CDATA처리를 위해 오버라이딩했다. . 
   */
  protected void writeText(QuickWriter writer, String text) {
    /**
     *  값이 없을 경우 <태그 /> 형태로 그냥 반환해버린다.
     *  차후 값이 없더라도 CDATA로 싸서 공백을 반환해야 하는 경우 아래 조건을 없애면 된다. 
     */ 
    iftext.trim().length() ){
      super.writeText(writer, text);
      return;
    }
    
    String CDATAPrefix = "<![CDATA[";
    String CDATASuffix = "]]>";

    /** 
     *  실제 CDATA를 추가해주는 부분
     *  CDATA에 대한 추가 로직이 필요하거나 CDATA사용이 필요없을 경우에는 아래 부분을 제거하지 말고 상위 클래스 사용을 권장한다.   
     */    
    if (!text.startsWith(CDATAPrefix&& !Pattern.matches("[^[0-9]]+", text)) {
      text = CDATAPrefix+text+CDATASuffix;
    }
    
    int length = text.length();
    if (!text.startsWith(CDATAPrefix)) {
      for (int i = 0; i < length; i++) {
        char c = text.charAt(i);
        switch (c) {
        case '&':
          writer.write(AMP);
          break;
        case '<':
          writer.write(LT);
          break;
        case '>':
          writer.write(GT);
          break;
        case '"':
          writer.write(QUOT);
          break;
        case '\'':
          writer.write(APOS);
          break;
        case '\r':
          writer.write(SLASH_R);
          break;
        default:
          writer.write(c);
        }
      }
    else {
      for (int i = 0; i < length; i++) {
        char c = text.charAt(i);
        writer.write(c);
      }
    }
  }
}

    • LocalXppDriver.java

package openframework.study.mapper.xstream.ext;

import java.io.Writer;

import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
 * XStream(http://xstream.codehaus.org/)을 사용하는 확장 XppDriver클래스
 
 @since 2008.05.30
 */
public class LocalXppDriver extends XppDriver {

  public HierarchicalStreamWriter createWriter(Writer out) {
    return new LocalPrettyPrintWriter(out);
  }
}

XStream사용시 주의할 점#

  • autodetectAnnotations() 메소드 사용
    • 동시성 문제를 발생시킬 가능성
    • model 객체내 모든 애노테이션을 감지하는 것은 불가능
    • processAnnotations() 메소드를 통해 애노테이션 처리가 필요한 클래스를 명시하는 방법이 적절함

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-6) was last changed on 01-May-2011 11:05 by DongGukLee