天天看點

三種java對象跨語言序列化反序列化實作與效率對比-上

    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執行個體的個數,縱軸表示耗時,機關毫秒。

三種java對象跨語言序列化反序列化實作與效率對比-上

下面是反序列化耗時:

其中橫軸代表Book執行個體的個數,縱軸表示耗時,機關毫秒。

三種java對象跨語言序列化反序列化實作與效率對比-上

下面是序列化後位元組大小:

橫軸是Book執行個體個數,縱軸是位元組個數,機關個。

三種java對象跨語言序列化反序列化實作與效率對比-上

    根據圖表分析,jprotobuf進行序列化時,耗時最長,但是反序列化耗時最短、序列化位元組長度最短。xml/jaxb反序列化耗時最長,序列化位元組長度最長。json/jackson表現較為适中。

繼續閱讀