Java编程思想学习笔记4 - 序列化技术

今天来学习下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更简单、快捷。

官方网站(http://hessian.caucho.com/)提供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。如下所示

 
    com.caucho
    hessian
    4.0.38
 
		
  • 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


    Hello
    com.caucho.hessian.server.HessianServlet
    
      home-class
      com.kcpt.hessian.service.IHelloImpl
    
    
      home-api
      com.kcpt.hessian.service.IHello
    
    1
  
  
    Hello
    /Hello
  

		
  • 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 = "http://127.0.0.1:8080/HessianService/Hello"; 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>xiazdongname> <age>20age> 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 friends = new ArrayList();
        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 friends = new ArrayList();
        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 friends = new ArrayList();
        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官方网站文档http://www.w3.org获取更多信息。

首先我们定义一个XML文档如下:

  <books> <book id="001"> <title>Harry Pottertitle> <author>J K. Rowlingauthor> book> <book id="002"> <title>Learning XMLtitle> <author>Erik T. Rayauthor> 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 nameList; private boolean title = false; public List 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();
        } // 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

参考文献

请使用浏览器的分享功能分享到微信等