Java程式設計思想學習筆記4 - 序列化技術

zhuyiquan90發表於2018-06-29

今天來學習下Java序列化和反序列化技術,筆者對《Java程式設計思想》中的內容,結合網上各位前輩的帖子進行了整理和補充,包括:

  • 序列化概述
  • Java原生序列化技術
  • Hessian序列化技術
  • JSON序列化技術
  • Java XML序列化技術

筆者將Hession、Json、XML都歸為是一種序列化技術(序列化的本質是“輕量級持久化”,而Hession、Json、XML都屬於這個範疇,Hessian、Json主要用於資料網路傳輸,XML用於資料介質的儲存)。內容還是比較豐富和完善的,部分內容直接參考了第三方部落格,參考文獻中有說明,希望大家指教!



1 序列化技術概述

物件的序列化是非常有意義的事情,利用它可以實現輕量級永續性(lightweight persistence)。“永續性”表明物件的生存週期不取決於程式是否正在執行,可以生存與程式的呼叫之間;“輕量級”表明序列化並不像ORM,需要顯示的在程式中進行序列化(serialize)和反序列化操作(deserialize)。

序列化和反序列化的概念

Serialize:把物件轉換為位元組序列的過程稱為物件的序列化。 
Deserialize:把位元組序列恢復為物件的過程稱為物件的反序列化。

為什麼需要序列化機制

在很多應用中,需要對某些物件進行序列化,讓它們離開記憶體空間,入住物理硬碟,以便長期儲存。比如最常見的是Web伺服器中的Session物件,當有 10萬使用者併發訪問,就有可能出現10萬個Session物件,記憶體可能吃不消,於是Web容器就會把一些seesion先序列化到硬碟中,等要用了,再把儲存在硬碟中的物件還原到記憶體中。

當兩個程式在進行遠端通訊時,彼此可以傳送各種型別的資料。無論是何種型別的資料,都會以二進位制序列的形式在網路上傳送。傳送方需要把這個Java物件轉換為位元組序列,才能在網路上傳送;接收方則需要把位元組序列再恢復為Java物件。

序列化的用途

資料介質儲存:把物件的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中; 
資料網路傳輸:在網路上傳送物件的位元組序列。


2 Java原生序列化技術

Java原生的序列化技術包括Serializable介面和Externalizable介面。

Serializable

Serializable的實現

只要物件實現了Serializable介面(該介面僅是一個標記介面,不包括任何方法),物件的序列化處理就會非常簡單。要序列化一個物件,首先要建立OutputStream物件,然後將其封裝在一個ObjectOutputStream物件內,再呼叫writeObject()方法即可物件序列化。反之,需要將一個InputStream封裝在ObjectInputStream內,然後呼叫readObject()方法反序列化。 
下面給一個例子:

package c18_Serial; import java.io.Serializable; public class Tester implements Serializable{ /**
     * 顯示地定義serialVersionUID
     */ private static final long serialVersionUID = -730422746011869282L; private String test; public String getTest() { return test;
    } public void setTest(String test) { this.test = test;
    }

}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
package c18_Serial; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializableDemo { public static void Serialize() throws IOException {
        Tester tester = new Tester();
        tester.setTest("序列化測試!");

        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("E:/Person.txt")));
        oo.writeObject(tester);
        System.out.println("Person物件序列化成功!");
        oo.close();
    } public static Tester Deserialize() throws IOException, ClassNotFoundException { @SuppressWarnings("resource")
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:/Person.txt")));
        Tester tester = (Tester) ois.readObject();
        System.out.println("Person物件反序列化成功!"); return tester;
    } public static void main(String[] args) throws IOException, ClassNotFoundException {
        Serialize();
        Tester tester = Deserialize();
    }
}

輸出結果:
Person物件序列化成功!
Person物件反序列化成功!
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

serialVersionUID的作用

serialVersionUID表示序列化的版本號,凡是實現Serializable介面的類都有一個表示序列化版本識別符號的靜態變數。serialVersionUID的取值是Java執行時環境根據類的內部細節自動生成的。如果對類的原始碼作了修改,再重新編譯,新生成的類檔案的serialVersionUID的取值有可能也會發生變化。

在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。

serialVersionUID分為兩種:

預設的serialVersionUID 
private static final long serialVersionUID = 1L;  
生成的serialVersionUID
 
private static final long serialVersionUID = xxxxL; 
根據類名、介面名、成員方法及屬性等來生成一個64位的雜湊欄位

類的serialVersionUID的預設值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的 serialVersionUID,也有可能相同。為了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,為它賦予明確的值。

transient關鍵字

當我們對序列化進行控制時,如果子物件表示的是我們不希望將其序列化的敏感資訊。面對這種情況,我們有兩種方案:

  1. 如果正在操作的是一個Serializable物件,為了能夠予以控制,可以用transient關鍵字逐個欄位關閉序列化;
  2. 將類實現為Externalizable,將在下節介紹。

transient關鍵字只能和Serializable物件一起使用。舉個例子:

package c18_Serial; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Date; import java.util.concurrent.TimeUnit; public class TransientDemo implements Serializable { private static final long serialVersionUID = 397689623972578855L; private Date date = new Date(); private String username; private transient String password; public TransientDemo(String name, String pwd) {
        username = name;
        password = pwd;
    } public String toString() { return "logon info:\n username:" + username + "\n date:" + date
                + "\n password:" + password;
    } public static void main(String[] args) throws FileNotFoundException,
            IOException, InterruptedException, ClassNotFoundException {
        TransientDemo t = new TransientDemo("Hulk", "myLittlePony");
        System.out.println("TransientDemo t = " + t);
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream( "TransientDemo.out"));
        o.writeObject(t);
        o.close();
        TimeUnit.SECONDS.sleep(1);

        ObjectInputStream in = new ObjectInputStream(new FileInputStream( "TransientDemo.out"));
        System.out.println("Recovering object t" + new Date());
        t = (TransientDemo)in.readObject();
        System.out.println("TransientDemo t = " +t);
        in.close();
    }
}
輸出結果:
TransientDemo t = logon info:
 username:Hulk
 date:Fri Jun 24 17:25:48 CST 2016 password:myLittlePony
Recovering object tFri Jun 24 17:25:49 CST 2016 TransientDemo t = logon info:
 username:Hulk
 date:Fri Jun 24 17:25:48 CST 2016 password:null
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

Externalizable

Externalizable介面繼承了Serializable介面,並增加了兩個方法writeExternal()和readExternal()來控制哪些屬性可以序列化,哪些不可以:在writeExternal()方法裡定義了哪些屬性可以序列化,哪些不可以序列化,所以,物件在經過這裡就把規定能被序列化的序列化儲存檔案,不能序列化的不處理,然後在反序列的時候自動呼叫readExternal()方法,根據序列順序挨個讀取進行反序列,並自動封裝成物件返回。

package c18_Serial; import java.io.Externalizable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.util.Date; public class ExternalizableDemo implements Externalizable{ private String username; private transient String password; public ExternalizableDemo(String name, String pwd) {
        username = name;
        password = pwd;
    } public String toString() { return "logon info:\n username:" + username 
                + "\n password:" + password;
    } @Override public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("執行序列化操作"); //可以在序列化時寫非自身的屬性 out.writeObject(new Date()); //只序列化username out.writeObject(username);
    } @Override public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        System.out.println("執行反序列化操作");
        Date d = (Date)in.readObject();
        System.out.println("Date: " + d);
        username = (String)in.readObject();
    } public static void main(String[] args) throws Exception, IOException {
        ExternalizableDemo e = new ExternalizableDemo("Hulk", "myLittlePony");
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream( "ExternalizableDemo.out"));
        o.writeObject(e);
        o.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream( "ExternalizableDemo.out"));
        System.out.println("Recovering object t" + new Date());
        e = (ExternalizableDemo)in.readObject();
        System.out.println("ExternalizableDemo t = " + e);
        in.close();
    }
} 
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

3 Hessian序列化技術

Hessian是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI(Remote Method Invoke 遠端方法呼叫)的功能,相比WebService,Hessian更簡單、快捷。

官方網站()提供Java、Flash/Flex、Python、C++、.NET C#等實現。Hessian和Axis、XFire都能實現web service方式的遠端方法呼叫,區別是Hessian是二進位制協議,Axis、XFire則是SOAP協議,所以從效能上說Hessian遠優於後兩者,並且Hessian的JAVA使用方法非常簡單。Hessian由於沒有WSDL這種服務描述檔案去對實現進行規定,似乎更適合內部分散式系統之間的互動,對外提供服務優於Axis、XFire。

Hessian的實現包括服務端和客戶端。

Hessian服務端實現

maven引入Hessian的jar包

透過maven引入Hessian的jar包,最新版本是4.0.38。如下所示

<!-- https://mvnrepository.com/artifact/com.caucho/hessian --> <dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.38</version>
</dependency> 
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

服務端介面與實現

服務端設計一個介面,用來給客戶端呼叫,並實現該介面的動能。

public interface IHello { String sayHello();
}
		
  • 1
  • 2
  • 3
public class IHelloImpl extends HessianServlet implements IHello { @Override public String sayHello() { // TODO Auto-generated method stub return "Hello,I from HessianService";
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

配置web.xml

配置web.xml,配置相應的servlet

<servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
    <init-param>
      <param-name>home-class</param-name>
      <param-value>com.kcpt.hessian.service.IHelloImpl</param-value>
    </init-param>
    <init-param>
      <param-name>home-api</param-name>
      <param-value>com.kcpt.hessian.service.IHello</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/Hello</url-pattern>
  </servlet-mapping>
</servlet>
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Hessian客戶端實現

  1. java客戶端包含Hessian.jar包

  2. 具有和伺服器端結構一樣的介面和實體類。包括名稱空間都最好一樣。將伺服器端打包放到客戶端,利用HessianProxyFactory呼叫遠端介面。

public class ClientTest { public static String url = ""; public static void main(String[] args){
        HessianProxyFactory factory = new HessianProxyFactory(); try {
            IHello iHello = (IHello) factory.create(IHello.class, url);
            System.out.println(iHello.sayHello());
        } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace();
        }
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Hessian與Spring結合

在實際應用中,我們不只是簡單的只使用Hessian來進行通訊的,如果方法多得話,還不如直接寫在客戶端來呼叫,然而:當Hessian與Spring結合後,大大減少了這些操作,將dao層的操作全部放在Hessian服務端,將業務邏輯全部放在Hessian客戶端,這樣的話我們的Hessian客戶端和服務端完全分離,因此我們的業務邏輯和dao層就真正的達到了分離,就可以放在不同的伺服器上,當然Hessian的通訊的作用不僅僅只有這些。

有關Hessian與Spring結合的具體實現,我們有機會透過具體的篇幅再分享,這裡不再深入。


4 JSON序列化技術

JSON是JavaScript Object Notation的縮寫,是一種輕量級的資料交換形式,是一種XML的替代方案,而且比XML更小,更快而且更易於解析。 
舉個簡單的例子

XML格式: <person> <name>xiazdong</name> <age>20</age> </person> JSON格式:
{ "name":"xiazdong", "age":20 }
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

JSON工具類有許多種,這裡列出三個比較流行的json工具類:Jackson,Gson,FastJson

Jackson

Jackson社群相對比較活躍,更新速度也比較快。Jackson對於複雜型別的json轉換bean會出現問題,一些集合Map,List的轉換出現問題。Jackson對於複雜型別的bean轉換Json,轉換的json格式不是標準的Json格式。

package serialize.json; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.ObjectMapper; import org.junit.After; import org.junit.Before; import org.junit.Test; import serialize.UserVo; public class JacksonTest { private UserVo user = null; private JsonGenerator jsonGenerator = null; private ObjectMapper objectMapper = null; @Before public void init()
    {
        user = new UserVo();
        user.setName("zzh");
        user.setAge(18);

        UserVo f1 = new UserVo();
        f1.setName("jj");
        f1.setAge(17);
        UserVo f2 = new UserVo();
        f2.setName("qq");
        f2.setAge(19);

        List<UserVo> friends = new ArrayList<UserVo>();
        friends.add(f1);
        friends.add(f2);
        user.setFriends(friends);

        objectMapper = new ObjectMapper(); try {
            jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(System.out,JsonEncoding.UTF8);
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    } @After public void destory()
    { try { if(jsonGenerator != null)
            {
                jsonGenerator.flush();
            } if(!jsonGenerator.isClosed())
            {
                jsonGenerator.close();
            }
            jsonGenerator = null;
            objectMapper = null;
            user = null;
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    } @Test public void writeJson()
    { try {
            jsonGenerator.writeObject(user);
            System.out.println();
            System.out.println(objectMapper.writeValueAsBytes(user).length); // System.out.println(objectMapper.writeValueAsString(user).length()); } catch (IOException e)
        {
            e.printStackTrace();
        }
    } @Test public void readJson()
    {
        String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17,\"friends\":null},{\"name\":\"qq\",\"age\":19,\"friends\":null}]}";
        UserVo uservo = null; try {
            uservo = objectMapper.readValue(serString, UserVo.class);
        } catch (IOException e)
        {
            e.printStackTrace();
        }
        System.out.println(uservo.getName());
    }
}

序列化大小:111
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

注意到這裡Jackson會輸出null,在Jackson的2.x版本中可以透過設定而使其不輸出null的欄位。

Gson

Gson是目前功能最全的Json解析神器,Gson當初是為因應Google公司內部需求而由Google自行研發而來,但自從在2008年五月公開發布第一版後已被許多公司或使用者應用。Gson的應用主要為toJson與fromJson兩個轉換函式,無依賴,不需要例外額外的jar,能夠直接跑在JDK上。而在使用這種物件轉換之前需先建立好物件的型別以及其成員才能成功的將JSON字串成功轉換成相對應的物件。類裡面只要有get和set方法,Gson完全可以將複雜型別的json到bean或bean到json的轉換,是JSON解析的神器。Gson在功能上面無可挑剔,但是效能上面比FastJson有所差距。

package serialize.json; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import serialize.UserVo; public class GsonTest { private UserVo user = null; @Before public void init()
    {
        user = new UserVo();
        user.setName("zzh");
        user.setAge(18);

        UserVo f1 = new UserVo();
        f1.setName("jj");
        f1.setAge(17);
        UserVo f2 = new UserVo();
        f2.setName("qq");
        f2.setAge(19);

        List<UserVo> friends = new ArrayList<UserVo>();
        friends.add(f1);
        friends.add(f2);
        user.setFriends(friends);
    } @Test public void writeJson()
    { try {
            String str = Gson.class.newInstance().toJson(user);//一行就可以搞定!!! System.out.println(str);
            System.out.println(str.length());
        } catch (InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
    } @Test public void readJson()
    {
        String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17},{\"name\":\"qq\",\"age\":19}]}"; try {
            UserVo userVo = Gson.class.newInstance().fromJson(serString, UserVo.class);
            System.out.println(userVo.getName());
        } catch (JsonSyntaxException | InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
    }
}

序列化大小:81
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

Gson和Jackson的區別是:如果你的應用經常會處理大的JSON檔案,那麼Jackson應該是你的菜。**GSON在大檔案上表現得相當吃力。如果你主要是處理小檔案請求,比如某個微服務或者分散式架構的初始化,那麼GSON當是首選。**Jackson在小檔案上的表現則不如人意。

FastJson

Fastjson是一個Java語言編寫的高效能的JSON處理器,由阿里巴巴公司開發。無依賴,不需要例外額外的jar,能夠直接跑在JDK上。 
FastJson在複雜型別的Bean轉換Json上會出現一些問題,可能會出現引用的型別,導致Json轉換出錯,需要制定引用。FastJson採用獨創的演算法,將parse的速度提升到極致,超過所有json庫。

package serialize.json; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import com.alibaba.fastjson.JSON; import serialize.UserVo; public class FastJsonTest { private UserVo user = null; @Before public void init()
    {
        user = new UserVo();
        user.setName("zzh");
        user.setAge(18);

        UserVo f1 = new UserVo();
        f1.setName("jj");
        f1.setAge(17);
        UserVo f2 = new UserVo();
        f2.setName("qq");
        f2.setAge(19);

        List<UserVo> friends = new ArrayList<UserVo>();
        friends.add(f1);
        friends.add(f2);
        user.setFriends(friends);
    } @Test public void writeJson()
    {
        String str = JSON.toJSONString(user);
        System.out.println(str);
        System.out.println(str.length());
    } @Test public void readJson()
    {
        String serString = "{\"name\":\"zzh\",\"age\":18,\"friends\":[{\"name\":\"jj\",\"age\":17},{\"name\":\"qq\",\"age\":19}]}";
        UserVo userVo = JSON.parseObject(serString,UserVo.class);
        System.out.println(userVo.getName());
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

如果只是功能要求,沒有效能要求,可以使用google的Gson,如果有效能上面的要求可以使用Gson將bean轉換json確保資料的正確,使用FastJson將Json轉換Bean。


5 Java XML序列化技術

XML現在已經成為一種通用的資料交換格式,它的平臺無關性,語言無關性,系統無關性,給資料整合與互動帶來了極大的方便。對於XML本身的語法知識與技術細節,需要閱讀相關的技術文獻,這裡麵包括的內容有DOM(Document Object Model), DTD(Document Type Definition), SAX(Simple API for XML),XSD(Xml Schema Definition),XSLT(Extensible Stylesheet Language Transformations),具體可參閱w3c官方網站文件獲取更多資訊。

首先我們定義一個XML文件如下:

 <?xml version="1.0" encoding="UTF-8"?> <books> <book id="001"> <title>Harry Potter</title> <author>J K. Rowling</author> </book> <book id="002"> <title>Learning XML</title> <author>Erik T. Ray</author> </book> </books>
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

DOM生成和解析XML文件

Java 中的 DOM 介面簡介: JDK 中的 DOM API 遵循 W3C DOM 規範,其中 org.w3c.dom 包提供了 Document、DocumentType、Node、NodeList、Element 等介面, 這些介面均是訪問 DOM 文件所必須的。我們可以利用這些介面建立、遍歷、修改 DOM 文件。 
javax.xml.parsers 包中的 DoumentBuilder 和 DocumentBuilderFactory 用於解析 XML 文件生成對應的 DOM Document 物件。 
javax.xml.transform.dom 和 javax.xml.transform.stream 包中 DOMSource 類和 StreamSource 類,用於將更新後的 DOM 文件寫入 XML 檔案。

  • 優點整個文件樹在記憶體中,便於操作;支援刪除、修改、重新排列等多種功能;
  • 缺點將整個文件調入記憶體(包括無用的節點),浪費時間和空間;
  • 使用場合:一旦解析了文件還需多次訪問這些資料;硬體資源充足(記憶體、CPU)。
import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class DOMParser { DocumentBuilderFactory builderFactory = DocumentBuilderFactory
            .newInstance(); // Load and parse XML file into DOM public Document parse(String filePath) {
        Document document = null; try { // DOM parser instance DocumentBuilder builder = builderFactory.newDocumentBuilder(); // parse an XML file into a DOM tree document = builder.parse(new File(filePath));
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } return document;
    } public static void main(String[] args) {
        DOMParser parser = new DOMParser();
        Document document = parser.parse("books.xml"); // get root element Element rootElement = document.getDocumentElement(); // traverse child elements NodeList nodes = rootElement.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) node; // process child element }
        }

        NodeList nodeList = rootElement.getElementsByTagName("book"); if (nodeList != null) { for (int i = 0; i < nodeList.getLength(); i++) {
                Element element = (Element) nodeList.item(i);
                String id = element.getAttribute("id");
            }
        }
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

SAX生成和解析XML文件

與 DOM 建立樹形結構的方式不同,SAX 採用事件模型來解析 XML 文件,是解析 XML 文件的一種更快速、更輕量的方法。 利用 SAX 可以對 XML 文件進行有選擇的解析和訪問,而不必像 DOM 那樣載入整個文件,因此它對記憶體的要求較低。 但 SAX 對 XML 文件的解析為一次性讀取,不建立任何文件物件,很難同時訪問文件中的多處資料。

  • 優點不用事先調入整個文件,佔用資源少。是解析 XML 文件的一種更快速、更輕量的方法。
  • 缺點:利用 SAX 可以對 XML 文件進行有選擇的解析和訪問,而不必像 DOM 那樣載入整個文件,因此它對記憶體的要求較低。 但 SAX 對 XML 文件的解析為一次性讀取,不建立任何文件物件,很難同時訪問文件中的多處資料。
  • 使用場合:Applet;只需XML文件的少量內容,很少回頭訪問;機器記憶體少;
import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; public class SAXParser { class BookHandler extends DefaultHandler { private List<String> nameList; private boolean title = false; public List<String> getNameList() { return nameList;
        } // Called at start of an XML document @Override public void startDocument() throws SAXException {
            System.out.println("Start parsing document...");
            nameList = new ArrayList<String>();
        } // Called at end of an XML document @Override public void endDocument() throws SAXException {
            System.out.println("End");
        } /**
         * Start processing of an element.
         * 
         * @param namespaceURI
         *            Namespace URI
         * @param localName
         *            The local name, without prefix
         * @param qName
         *            The qualified name, with prefix
         * @param atts
         *            The attributes of the element
         */ @Override public void startElement(String uri, String localName, String qName,
                Attributes atts) throws SAXException { // Using qualified name because we are not using xmlns prefixes // here. if (qName.equals("title")) {
                title = true;
            }
        } @Override public void endElement(String namespaceURI, String localName,
                String qName) throws SAXException { // End of processing current element if (title) {
                title = false;
            }
        } @Override public void characters(char[] ch, int start, int length) { // Processing character data inside an element if (title) {
                String bookTitle = new String(ch, start, length);
                System.out.println("Book title: " + bookTitle);
                nameList.add(bookTitle);
            }
        }

    } public static void main(String[] args) throws SAXException, IOException {
        XMLReader parser = XMLReaderFactory.createXMLReader();
        BookHandler bookHandler = (new SAXParser()).new BookHandler();
        parser.setContentHandler(bookHandler);
        parser.parse("books.xml");
        System.out.println(bookHandler.getNameList());
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

DOM4J生成和解析XML文件

dom4j是一個Java的XML API,類似於jdom,用來讀寫XML檔案的。dom4j是一個非常非常優秀的Java XML API,具有效能優異、功能強大和極端易用使用的特點,同時它也是一個開放原始碼的軟體,可以在SourceForge上找到它.

對主流的Java XML API進行的效能、功能和易用性的評測,dom4j無論在那個方面都是非常出色的。如今你可以看到越來越多的Java軟體都在使用dom4j來讀寫XML,例如Hibernate,包括sun公司自己的JAXM也用了Dom4j。

舉個例子,使用Iterator迭代器的方式來解析xml:

import java.io.File; import java.util.Iterator; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class Demo { public static void main(String[] args) throws Exception {
        SAXReader reader = new SAXReader();
        Document document = reader.read(new File("books.xml"));
        Element root = document.getRootElement();

        Iterator it = root.elementIterator(); while (it.hasNext()) {
            Element element = (Element) it.next(); // 未知屬性名稱情況下 /*
             * Iterator attrIt = element.attributeIterator(); while
             * (attrIt.hasNext()) { Attribute a = (Attribute) attrIt.next();
             * System.out.println(a.getValue()); }
             */ // 已知屬性名稱情況下 System.out.println("id: " + element.attributeValue("id")); // 未知元素名情況下 /*
             * Iterator eleIt = element.elementIterator(); while
             * (eleIt.hasNext()) { Element e = (Element) eleIt.next();
             * System.out.println(e.getName() + ": " + e.getText()); }
             * System.out.println();
             */ // 已知元素名情況下 System.out.println("title: " + element.elementText("title"));
            System.out.println("author: " + element.elementText("author"));
            System.out.println();
        }
    }
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

參考文獻

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31541037/viewspace-2157022/,如需轉載,請註明出處,否則將追究法律責任。

相關文章