java對象常用的跨語言序列化反序列化主要有三種:一是xml形式;二是json形式;三是protobuf位元組流形式。本篇文章主要介紹這三種序列化反序列化方式的實作和其效率對比。
首先介紹xml形式的序列化與反序列化,使用jaxb來實作。JAXB能夠使用Jackson對JAXB注解的支援實作(jackson-module-jaxb-annotations),既友善生成XML,也友善生成JSON,這樣一來可以更好的标志可以轉換為JSON對象的JAVA類。JAXB允許JAVA人員将JAVA類映射為XML表示方式,常用的注解包括:@XmlRootElement,@XmlElement等等。其maven依賴為:
<!-- http://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<!-- http://mvnrepository.com/artifact/javax.xml/jaxb-api -->
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>
<!-- http://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
定義的pojo類有兩個:第一個是Book.java,另一個為BookList.java,是Book的集合類。隻需要在定義的類名上添加@XmlRootElement注解即可,對業務代碼沒有侵入性。其中兩個類中的屬性值上有@Protobuf注解,這是protobuf序列化需要的,對xml序列化無影響。
Book類代碼:
package zhangq.pojo;
import javax.xml.bind.annotation.XmlRootElement;
import com.baidu.bjf.remoting.protobuf.FieldType;
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
@XmlRootElement
public class Book {
@Protobuf(fieldType = FieldType.INT32, order=1)
int id;
@Protobuf(fieldType = FieldType.STRING, order=2)
String name;
@Protobuf(fieldType = FieldType.STRING, order=3)
String address;
@Protobuf(fieldType = FieldType.STRING, order=4)
String buyer;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getBuyer() {
return buyer;
}
public void setBuyer(String buyer) {
this.buyer = buyer;
}
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", address=" + address + ", buyer=" + buyer + "]";
}
}
BookList類代碼:
package zhangq.pojo;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import com.baidu.bjf.remoting.protobuf.annotation.Protobuf;
@XmlRootElement
public class BookList{
@Protobuf(order=1)
private List<Book> books;
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
@Override
public String toString() {
return "BookList [books=" + books + "]";
}
}
定義完pojo後,就可以進行xml的序列化與反序列化實作了。
首先定義一個測試輔助類,用來生成pojo執行個體:
package zhangq.test;
import java.util.ArrayList;
import java.util.List;
import zhangq.pojo.Book;
import zhangq.pojo.BookList;
public class TestHelper {
public static BookList buildBookList(int count){
BookList bookList = new BookList();
List<Book> books = new ArrayList<Book>();
for (int i=0; i<count; i++){
books.add(buildBook(i));
}
bookList.setBooks(books);
return bookList;
}
public static Book buildBook(int id){
Book book = new Book();
book.setAddress("Peking");
book.setBuyer("hello");
book.setId(id);
book.setName("Netty");
return book;
}
}
然後,定義一個性能記錄類,用來記錄序列化與反序列化的耗時:
package zhangq.util;
import org.apache.log4j.Logger;
public class PerformanceRecord {
private static Logger m_Logger = Logger.getLogger(PerformanceRecord.class);
private String desp;
private long iStart;
private PerformanceRecord(String desp){
this.desp = desp;
}
public static PerformanceRecord getInstance(String desp){
return new PerformanceRecord(desp);
}
public long start(){
iStart = System.nanoTime();
return iStart;
}
public double endInSeconds(){
long iEnd = System.nanoTime();
double timeInSeconds = (iEnd - iStart) / (1000000000.0);
m_Logger.info(desp + " 耗時為 [" + timeInSeconds + "] 秒");
return timeInSeconds;
}
public double endInMs(){
long iEnd = System.nanoTime();
double timeInMs = (iEnd - iStart) / (1000000.0);
m_Logger.info(desp + " 耗時為 [" + timeInMs + "] 毫秒");
return timeInMs;
}
public long endInNs(){
long iEnd = System.nanoTime();
long timeInNs = (iEnd - iStart);
m_Logger.info(desp + " 耗時為 [" + timeInNs + "] 納秒");
return timeInNs;
}
}
使用jaxb進行xml序列化的代碼為:
public static String testMarshallList(int count) throws Exception{
BookList books = TestHelper.buildBookList(count);
JAXBContext jaxbContext = JAXBContext.newInstance(BookList.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
OutputStream outputStream = new ByteArrayOutputStream();
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("XmlMashall");
performanceRecord.start();
marshaller.marshal(books, outputStream);
performanceRecord.endInMs();
String string = outputStream.toString();
outputStream.close();
//m_Logger.debug("ObjToXml:");
//m_Logger.debug(string);
return string;
}
使用jaxb進行xml反序列化的代碼為:
public static BookList testUnMarshallList(String string) throws Exception{
JAXBContext jaxbContext = JAXBContext.newInstance(BookList.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(string.getBytes());
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("XmlUnmashall");
performanceRecord.start();
BookList books = (BookList)unmarshaller.unmarshal(inputStream);
performanceRecord.endInMs();
//m_Logger.debug("XmlToObj:");
//m_Logger.debug(books.toString());
return books;
}
其次介紹json的序列化,使用jackson實作。使用jackson,不需要對pojo進行任何修改,不需要添加注解。其maven依賴為:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
jackson的json序列化代碼為:
public static String testObjToJsonList(int count) throws Exception{
String string = null;
BookList books = TestHelper.buildBookList(count);
ObjectMapper mapper = new ObjectMapper();
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("Jsonmashall");
performanceRecord.start();
string = mapper.writeValueAsString(books);
performanceRecord.endInMs();
// m_Logger.debug("ObjToJson:");
// m_Logger.debug(string);
return string;
}
jackson的json反序列化代碼為:
public static BookList testJsonToObjList(String string) throws Exception{
BookList books = null;
ObjectMapper mapper = new ObjectMapper();
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("JsonUnmashall");
performanceRecord.start();
books = mapper.readValue(string, BookList.class);
performanceRecord.endInMs();
// m_Logger.debug("JsonToObj:");
// m_Logger.debug(books.toString());
return books;
}
最後是protobuf位元組流的序列化與反序列化,使用jprotobuf庫進行,jprotobuf庫的優點是使用簡單,隻需要在pojo上添加@protobuf注解即可,關于jprotobuf的介紹請看其git:https://github.com/jhunters/jprotobuf/ 。其maven依賴為:
<!-- https://mvnrepository.com/artifact/com.baidu/jprotobuf -->
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.1.8</version>
</dependency>
jprotobuf序列化代碼為:
public static byte[] testObjToByteList(int count){
Codec<BookList> simpleTypeCodec = ProtobufProxy
.create(BookList.class);
BookList books = TestHelper.buildBookList(count);
byte[] bytes = null;
try {
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("ProtoBufMashall:");
performanceRecord.start();
bytes = simpleTypeCodec.encode(books);
performanceRecord.endInMs();
} catch (IOException e) {
// TODO Auto-generated catch block
m_Logger.error(e.toString());
}
return bytes;
}
jprotobuf反序列化代碼為:
public static BookList testByteToObjList(byte[] bytes){
Codec<BookList> simpleTypeCodec = ProtobufProxy
.create(BookList.class);
BookList books = null;
try{
PerformanceRecord performanceRecord = PerformanceRecord.getInstance("ProtoBufUnMashall:");
performanceRecord.start();
books = simpleTypeCodec.decode(bytes);
performanceRecord.endInMs();
}catch (Exception e){
m_Logger.error(e.toString());
}
return books;
}
最後,需要對這三種方式的序列化與反序列化耗時進行記錄,并對其序列化後的位元組大小進行比較。需要注意的是,這三種方式都會涉及到預處理,并且内部有緩存等優化機制,是以對每種的序列化與反序列化函數運作兩次,取第二次的耗時,這更符合實際工程運作時的耗時。 下面是序列化耗時: 其中橫軸代表Book執行個體的個數,縱軸表示耗時,機關毫秒。
下面是反序列化耗時:
其中橫軸代表Book執行個體的個數,縱軸表示耗時,機關毫秒。
下面是序列化後位元組大小:
橫軸是Book執行個體個數,縱軸是位元組個數,機關個。
根據圖表分析,jprotobuf進行序列化時,耗時最長,但是反序列化耗時最短、序列化位元組長度最短。xml/jaxb反序列化耗時最長,序列化位元組長度最長。json/jackson表現較為适中。