使用XStream是實現XML與Java對象的轉換(3)--註解

六、使用註解(Annotation)

總是使用XStream對象的別名方法和註冊轉換器,會讓人感到非常的乏味,又會產生很多重複性代碼,於是我們可以使用註解的方式來配置要序列化的POJO對象。

1,最基本的註解:類的別名性註解和字段的別名性註解(XStreamAlias)

 

有這樣一段代碼:

import com.thoughtworks.xstream.XStream;
 
public class XStreamTest3 {
   public static void main(String[] args) {
      XStream stream = new XStream();
      RendezvousMessage msg = new RendezvousMessage(15);
      System.out.println(stream.toXML(msg));
   }
 
}
class RendezvousMessage {
  
   private int messageType;
  
   public RendezvousMessage(int messageType) {
      this.messageType = messageType;
   }
}

 

運行結果是:

<cn.tjpu.zhw.xml.RendezvousMessage>
  <messageType>15</messageType>
</cn.tjpu.zhw.xml.RendezvousMessage>

 

 

如果我們需要將輸出的XML文本是這樣:

<message>
  <type>15</type>
</message>

 

該怎麼辦?

我們當然可以在main方法中調用XStream對象的別名映射方法進行處理,但我們也可以使用更簡單的註解的方式進行處理。

RendezvousMessage類的定義進行註解如下:

//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
  
   public RendezvousMessage(int messageType) {
      this.messageType = messageType;
   }
}

 

但是,我們進行註解之後發現輸出的結果並沒有改變,爲什麼?

因爲XStream對象默認是不讀取和識別註解的,需要我們主動提醒它,而後XStream對象才能在轉換的的時候讀取註解。

更改main方法如下:

public static void main(String[] args) {
      XStream stream = new XStream();
      //通知XStream對象讀取並識別RendezvousMessage中的註解
      stream.processAnnotations(RendezvousMessage.class);
      RendezvousMessage msg = new RendezvousMessage(15);
      System.out.println(stream.toXML(msg));
}

 

這樣輸出的結果就能與預想的一樣了。

注意:當使用XStream對象處理一個被註解的類型時,XStream對象也會處理所有與其相關的類型的註解信息,即該類型的父類、父接口、所有子類的註解。

processAnnotations方法還有一個重載的方法,是以Class []作爲參數的。

2,隱式集合註解(XStreamImplicit)

現在我們給RendezvousMessage類添加一個List集合字段,並且更改一下RendezvousMessage的構造方法,新的代碼如下:

import java.util.Arrays;
import java.util.List;
 
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
 
public class XStreamTest3 {
   public static void main(String[] args) {
      XStream stream = new XStream();
      //通知XStream對象讀取並識別RendezvousMessage中的註解
      stream.processAnnotations(RendezvousMessage.class);
      RendezvousMessage msg = new RendezvousMessage(15,"first","second");
      System.out.println(stream.toXML(msg));
   }
 
}
 
//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
  
   //新添加的集合字段
   private List<String> content;
  
   //經改造的構造方法
   public RendezvousMessage(int messageType, String ... content) {
      this.messageType = messageType;
      this.content = Arrays.asList(content);
   }
}

 

運輸出結果如下:

<message>
  <type>15</type>
  <content class="java.util.Arrays$ArrayList">
    <a class="string-array">
      <string>first</string>
      <string>second</string>
    </a>
  </content>
</message>

 

但是,如果我們想讓輸出的XML格式如下:

<message>
  <type>15</type>
  <part>firstPart</part>
  <part>secondPart</part>
</message>

 

該怎麼辦?

現在我們給集合字段添加隱式集合性註解,以去除集合的根節點:

//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
  
   //隱式集合性註解
   @XStreamImplicit
   private List<String> content;
  
   public RendezvousMessage(int messageType, String ... content) {
      this.messageType = messageType;
      this.content = Arrays.asList(content);
   }
}

 

重新運行程序,輸出結果如下:

<message>
  <type>15</type>
  <string>first</string>
  <string>second</string>
</message>

 

輸出的結果中,集合的每一個子節點的節點名都是string,現在需要將子節點的節點名改爲part,這樣就需要繼續更改註解項:

//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
  
   //對隱式集合的註解,將每一個子節點的節點名都改爲part
   @XStreamImplicit(itemFieldName="part")
   private List<String> content;
  
   public RendezvousMessage(int messageType, String ... content) {
      this.messageType = messageType;
      this.content = Arrays.asList(content);
   }
}

 

這樣輸出的結果就能夠跟預想的一樣了,成功了!!!

注意:隱式集合註解同樣可以用於數組和Map對象。

3,註解轉換器(XStreamConverter)

現在我們再給RendezvousMessage類添加兩個字段,一個boolean字段和一個時間Calendar字段,代碼如下:

public class XStreamTest3 {
   public static void main(String[] args) {
      XStream stream = new XStream();
      // 通知XStream對象讀取並識別RendezvousMessage中的註解
      stream.processAnnotations(RendezvousMessage.class);
      RendezvousMessage msg = new RendezvousMessage(15,false,"first","second");
      System.out.println(stream.toXML(msg));
   }
 
}
 
// 對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
 
   // 對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
 
   // 隱式集合性註解
   @XStreamImplicit(itemFieldName = "part")
   private List<String> content;
 
   private boolean important;
 
   private Calendar created = new GregorianCalendar();
 
   // 再次對構造方法進行了改造
   public RendezvousMessage(int messageType, boolean important,
        String... content) {
      this.messageType = messageType;
      this.important = important;
      this.content = Arrays.asList(content);
   }
 
}

 

運行結果如下:

<message>
  <type>15</type>
  <part>first</part>
  <part>second</part>
  <important>false</important>
  <created>
    <time>1387534087343</time>
    <timezone>Asia/Shanghai</timezone>
  </created>
</message>

 

現在,我們要將輸出結果改造爲:

<message>
  <type>15</type>
  <part>firstPart</part>
  <part>secondPart</part>
  <important>no</important>
  <created>1379430873703</created>
</message>

 

該如何做?

首先,我們使用註解處理Calendar時間字段的轉換,先定義一個時間的轉換器SingleValueCalendarConverter,代碼如下:

package cn.tjpu.zhw.xml;
 
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
 
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 
//必須是public類型
public class SingleValueCalendarConverter implements Converter {
 
    public void marshal(Object source, HierarchicalStreamWriter writer,
            MarshallingContext context) {
        Calendar calendar = (Calendar) source;
        writer.setValue(String.valueOf(calendar.getTime().getTime()));
    }
 
    public Object unmarshal(HierarchicalStreamReader reader,
            UnmarshallingContext context) {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(new Date(Long.parseLong(reader.getValue())));
        return calendar;
    }
 
    public boolean canConvert(Class type) {
        return type.equals(GregorianCalendar.class);
    }
}

 

 

然後,我們需要使用SingleValueCalendarConverter轉換器對Calendar字段進行註解:

//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
  
   //對隱式集合的註解,將每一個子節點的節點名都改爲part
   @XStreamImplicit(itemFieldName="part")
   private List<String> content;
  
   private boolean important;
  
   //爲該字段的註解指定轉換器
   @XStreamConverter(SingleValueCalendarConverter.class)
   private Calendar created = new GregorianCalendar();
  
   public RendezvousMessage(int messageType, String ... content) {
      this.messageType = messageType;
      this.content = Arrays.asList(content);
   }
}

 

運行結果如下:

<message>
  <type>15</type>
  <part>first</part>
  <part>second</part>
  <important>false</important>
  <created>1387534774062</created>
</message>

 

 

但是我們發現important節點中的內容是truefalse,怎樣讓它變成yesno呢?

我們可以使用框架爲我們提供的一個轉換器BooleanConverter

修改RendezvousMessage的類定義:

//對類別名的註解
@XStreamAlias("message")
class RendezvousMessage {
  
   //對字段別名的註解
   @XStreamAlias("type")
   private int messageType;
  
   //對隱式集合的註解,將每一個子節點的節點名都改爲part
   @XStreamImplicit(itemFieldName="part")
   private List<String> content;
  
   //將true/false改爲yes/no
   @XStreamConverter(value=BooleanConverter.class, booleans={false}, strings={"yes", "no"})
   private boolean important;
  
   //爲該字段添加轉換器註解
   @XStreamConverter(SingleValueCalendarConverter.class)
   private Calendar created = new GregorianCalendar();
  
   public RendezvousMessage(int messageType, boolean important, String... content) {
      this.messageType = messageType;
      this.important = important;
      this.content = Arrays.asList(content);
   }
}

 

運行結果如下:

<message>
  <type>15</type>
  <part>first</part>
  <part>second</part>
  <important>no</important>
  <created>1387534827609</created>
</message>

 

這正是我們想要的!!!!

4,屬性註解

現在我們想將上面的XML格式改造成爲:

<message type="15" important="no">
  <part>firstPart</part>
  <part>secondPart</part>
  <created>1154097812245</created>
</message>

 

,也就是把type節點和important節點作爲父節點的屬性,該怎麼做?

答案是,使用屬性註解:

@XStreamAsAttribute

 

代碼如下:

public class XStreamTest3 {
   public static void main(String[] args) {
      XStream stream = new XStream();
      // 通知XStream對象讀取並識別RendezvousMessage中的註解
      stream.processAnnotations(RendezvousMessage.class);
      RendezvousMessage msg = new RendezvousMessage(15, false, "first",
           "second");
      System.out.println(stream.toXML(msg));
   }
 
}
 
// 對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
 
   //將type節點變成屬性
   @XStreamAsAttribute
   // 對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
 
   // 隱式集合性註解
   @XStreamImplicit(itemFieldName = "part")
   private List<String> content;
 
  
   //將important節點變成屬性
   @XStreamAsAttribute
   // 將true/false改爲yes/no
   @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {
        "yes", "no" })
   private boolean important;
 
   @XStreamConverter(SingleValueCalendarConverter.class)
   private Calendar created = new GregorianCalendar();
 
   public RendezvousMessage(int messageType, boolean important,
        String... content) {
      this.messageType = messageType;
      this.important = important;
      this.content = Arrays.asList(content);
   }
}
 

 

結果是:

<message type="15" important="no">
  <part>first</part>
  <part>second</part>
  <created>1387540760390</created>
</message>

 

我們有成功了!!!!

 

5,使用註解將字段轉換爲父節點文本內容

我們如果想得到的XML是如下形式:

<message type="15" important="no" created="1154097812245">This is the message content.</message>

 

就是將typeimportantcreated三個節點全部變屬性,並且將content節點的內容變爲父節點message的內容,如何做?

這就需要用到

ToAttributedValueConverter轉換器註解

代碼如下:

// 新加的轉換器註解
@XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" })
// 對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
 
   // 將type節點變成屬性
   @XStreamAsAttribute
   // 對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
 
   // 隱式集合性註解
   @XStreamImplicit(itemFieldName = "part")
   private List<String> content;
 
   // 將important節點變成屬性
   @XStreamAsAttribute
   // 將true/false改爲yes/no
   @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {
        "yes", "no" })
   private boolean important;
 
   @XStreamConverter(SingleValueCalendarConverter.class)
   private Calendar created = new GregorianCalendar();
 
   public RendezvousMessage(int messageType, boolean important,
        String... content) {
      this.messageType = messageType;
      this.important = important;
      this.content = Arrays.asList(content);
   }
}

 

但是運行之後,會發現,運行結果根本與我們預期的不一樣,爲什麼?

因爲ToAttributedValueConverter轉換器接受的content節點必須是String類型或者有一個轉換器將content裝換爲String類型!!!

例如,將content節點變爲String類型:

//新加的轉換註解
@XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" })
//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
 
   // 對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
 
   //由原來的List<String>類型變爲String類型
   private String content;
  
 
   // 將true/false改爲yes/no
   @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {
        "yes", "no" })
   private boolean important;
 
// @XStreamConverter(SingleValueCalendarConverter.class)
// private Calendar created = new GregorianCalendar();
 
   // 再次對構造方法進行了改造
   public RendezvousMessage(int messageType, boolean important,
        String content) {
      this.messageType = messageType;
      this.important = important;
      this.content = content;
   }
}

 

運行結果爲:

<message type="15" important="no">這是一大串content節點的內容</message>

 

雖然typeimportant節點沒有使用@XStreamAsAttribute註解,但是卻被隱式的轉換爲屬性。

 

6,使用註解忽略某些字段

忽略messageType字段可以使用@XStreamOmitField註解

代碼如下:

//對類的別名性註解
@XStreamAlias("message")
class RendezvousMessage {
 
   //忽略messageType字段
   @XStreamOmitField
   // 將type節點變成屬性
   @XStreamAsAttribute
   // 對字段的別名性註解
   @XStreamAlias("type")
   private int messageType;
 
   // 隱式集合性註解
   @XStreamImplicit(itemFieldName = "part")
   private List<String> content;
  
 
   // 將important節點變成屬性
   @XStreamAsAttribute
   // 將true/false改爲yes/no
   @XStreamConverter(value = BooleanConverter.class, booleans = { false }, strings = {
        "yes", "no" })
   private boolean important;
 
   @XStreamConverter(SingleValueCalendarConverter.class)
   private Calendar created = new GregorianCalendar();
 
   // 再次對構造方法進行了改造
   public RendezvousMessage(int messageType, boolean important,
        String... content) {
      this.messageType = messageType;
      this.important = important;
      this.content = Arrays.asList(content);
   }
}

 

運行結果:

<message important="no">
  <part>first</part>
  <part>second</part>
  <created>1387544212500</created>
</message>

 

 

7,自動檢測註解

之前我們啓用某個類的註解時,都需要使用processAnnotations方法通知xstream對象解析註解類,其實我們還有一個更簡便的模式,即調用autodetectAnnotations(true)方法,讓xstream對象自動檢測註解類:

public class XStreamTest3 {
   public static void main(String[] args) {
      XStream stream = new XStream();
//    // 通知XStream對象讀取並識別RendezvousMessage中的註解
//    stream.processAnnotations(RendezvousMessage.class);
      //自動檢測註解
      stream.autodetectAnnotations(true);
      RendezvousMessage msg = new RendezvousMessage(15, false, "first","second");
      System.out.println(stream.toXML(msg));
   }
}

 

注意:1,自動檢測註解模式,會使XStream的解析變慢!2,在任何地方調用processAnnotations方法之後,自動檢測註解模式將會被關閉。

 

 

 

 

 

 

 

 

 

發佈了37 篇原創文章 · 獲贊 2 · 訪問量 6031
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章