搜档网
当前位置:搜档网 › jaxb使用文档

jaxb使用文档

JAXB2.0 简介

JAXB(java Architecture for XML Binding)是java提供处理XML与java对象映射的标准库。在javaSE6.0中已经包含了JAXB定义的所有API,在处理XML文档时,使用JAXB要比使用JAXP(DOM、SAX和StAX)更方便。(但不表明DOM、SAX和StAX就再无用武之地,并且JAXB的实现也是基于JAXP)。

JAXB框架中提供两种编写XML和java对象交互的方法。其一,通过XSD(XML Schema Definition)文档生成Java类的方式。其二,通过编写标注java类,之后生成相应的XSD文档(第二种,生成XSD文档并不是必须的)。JAXB通过分析java类中的标记(annotation),将java类转换为相应的XML文档。在方案一中,通过XSD文件生成的java类,已经被编译器(xjc)进行了标注,因此无需手工标注,而对应第二种方案,必须是编程人员手工添加这些标记。JAXB定义的标记比较多,也很复杂,要想灵活的控制转换过程,可以使用直接标注的方式。使用XSD转换的方式,相对比较简单,但需要使用者具有熟练的XSD编写经验,此外,使用XSD方式生成XML文档还有一大优势,就是在分布式系统中,不能保证XML信息的发送端和接收端都是使用的java运行环境,因此,XSD实际上将会成为端到端通信的一个标准,规定了通信的信息格式,因此,一般在分布式系统中可以先编写XSD文件,之后通信两端根据XSD提供的协议各自根据自身的情况,实现对XML文档的处理。

下文采用第一种方式对JAXB的使用方法进行介绍。编写示例的流程是,先编写XSD文件,通过编译器(xjc XML to Java Compiler)编译生成java类,编写测试程序(一般名称叫ProcessXML),最后给出执行结果。

本文内容源于JAXB官方网站提供的帮助文档https://www.sodocs.net/doc/ef9281446.html,/tutorial/section_1_1-Introduction.html#Introduction,本文并不是对原有官方文档进行简单的翻译,而是挑选出源文档中具有代表性的内容,进行较深入的讲解,本文提供的实例与原文档提供的实例完全不同,是作者根据相应内容进行设计的,作者希望这些实例能够提供对原文档的一个补充说明。读者可以不借助原文档直接阅读下面的内容,因为本文内容基本可以自成体系,此外本文保持了原文档的编号方式,方便读者到原文档中查找相应内容。由于作者的局限性,很多地方仍有不足,因此作者不保证示例和评论的正确性,请读者自

行验证。

在本文的最后提供了本文实例程序的打包下载,如果您想要亲自测试这些程序,可以从提供的连接上下载到文档。

目录:

1.3 第一个jaxb程序(一个完整的入门实例,介绍了使用XSD生成java类,并进行编组解组的全过程)

2 为JAXB编写Schema(简单介绍schema,并提供学习网站)

2.2.2 schema的时间日期类型(如何将schema中的时间和日期,转换到java类对应的数据结构)

2.2.7 schema的list元素(schema的list元素可以将简单数据类型压缩成一个字符串,减少传输的数据量)

2.2.9 使用枚举类型(schema的枚举类型如何与java的枚举类型映射)

2.2.12.4 使用schema的choice类型(schema的choice元素与java类转换)2.2.12.5 同构集合(如何映射java的同构集合)

2.2.13 schema编写继承关系(设计了一个复杂继承关系的例子,并且使用了设计模式中的组合模式,整个程序比较复杂,也是理解JAXB的一个关键)

2.2.15 schema的ID和IDREF (对应java类内部的循环引用问题)

4.4 XML文档传输java对象(将串行化的java对象放入xml文档中编组,解组后在将数据反串行化)

2.3.2 一个schema引用另外一个schema (关于schema命名空间的使用说明)

1.3 第一个JAXB程序

现在我们想将一个学生的信息编组成XML文档,再从XML文档中解组成学生对象,学生信息包括了学号属性和学生姓名元素。从这个示例中,介绍了使用XSD生成java类,并进行编组和解组的全过程,这是一个相当于Hello World一样的入门程序。

先编写一个XSD文档:

xsd1_3 : Student1_3.xsd

使用xjc(XML java compiler)编译生成java类:

I:\programs\eclipse\JAXBDemos\src>xjc -p https://www.sodocs.net/doc/ef9281446.html,.upc.upcgrid.guan.jaxb.demo1_3.Student Student1_3.xsd 将编译好的xsd放到了JAXBDemos工程的src目录下。xjc –p 后面附加java 类的包名和xsd文件的路径。

编译后生成的java类:

Code1-3-1:Student.java

package https://www.sodocs.net/doc/ef9281446.html,.upc.upcgrid.guan.jaxb.demo1_3.Student;

import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlAttribute;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;

import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "", propOrder = {

"name"

})

@XmlRootElement(name = "Student")

public class Student {

@XmlElement(required = true)

protected String name;

@XmlAttribute

protected Integer id;

public String getName() {

return name;

}

public void setName(String value) {

https://www.sodocs.net/doc/ef9281446.html, = value;

}

public Integer getId() {

return id;

}

public void setId(Integer value) {

this.id = value;

}

}

Code1-3-2 ObjectFactory.java

package https://www.sodocs.net/doc/ef9281446.html,.upc.upcgrid.guan.jaxb.demo1_3.Student;

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry

public class ObjectFactory {

public ObjectFactory() {

}

public Student createStudent() {

return new Student();

}

}

根据xsd1_3的定义,xjc生成了两个类,分别是Student和ObjectFactory。通过xjc编译生成java类时都会产生一个ObjectFactory类,在JAXB进行解组时会调用这个工厂类创建其管辖产品类的对象。ObjectFactory类提供了所有子产品的无参构造函数。可以在工厂类中提供一些方便编程使用的额外方法,比如带参数的构造方法和一些对产品类进行的额外包装的方法等。

下面是针对xjc生成java类的测试代码:

Code1-3-3 XMLParser.java

package https://www.sodocs.net/doc/ef9281446.html,.upc.upcgrid.guan.jaxb.demo1_3.Student;

import java.io.File;

import javax.xml.bind.JAXBContext;

import javax.xml.bind.JAXBException;

import javax.xml.bind.Marshaller;

public class XMLParser {

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();//对象工厂

Student student = of.createStudent();//可以使用对象工厂创建对象

File file = new File("student1_3.xml");//用于输出的XML文档

student.setId(5566);//设置学生属性

student.setName("Mary");

JAXBContext context = JAXBContext.newInstance(Student.class);//创建JAXB环境

Marshaller marshaller = context.createMarshaller();//创建编组

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);//设置编组属性,使得输出的XML文档进行格式化(提供缩进)marshaller.marshal(student, file);//编组到文件

Student newStudent = (Student)

context.createUnmarshaller().unmarshal(file);//从文件解组到一个新对象中

System.out.println(newStudent.getName());//核实信息

System.out.println(newStudent.getId());

}

}

JAXBContext是创建Marshaller和Unmarshaller的上下文环境,在这个环境中要配置那些使用XML annotation标记过的类(只提供顶级类(相当于根,其他的类都能通过这个类推演出来)即可)或包名,在配置完成后,context会加载相应的类,处理分析类中的标记,并将处理结果进行缓存,以备编码和解码使用。调用context的create方法来创建Marshaller和Unmarshaller。最后使用创建的Marshaller和Unmarshaller进行编组和解组。

生成XML文档:

XML1-3 student1_3.xml

Mary

标准输出:

Out1-3:

Mary

5566

说明:在后面的内容中,我们不会将所有的代码贴出,尤其是xjc生成的代码。实际上,我们更关心XSD文档的定义,以及对xjc生成代码的使用。想要查询完全的代码,请到文章开始处提供的连接进行下载。

2 为JAXB编写schema

本文不打算讲解schema的定义和语法,您可以到https://www.sodocs.net/doc/ef9281446.html,/schema/网站上找到关于Schema文档定义所需的知识,在您继续阅读下面的内容之前,我希望您能够浏览一下上面提供的链接中关于Schema的内容,如果您是Schema的新手,相信w3school会给你提供足够的内容让您了解Schema的基本使用方法。需要注意的是,使用xjc不能对Schema 的Facets(限定)进行验证,您需要提供特殊的验证。由于篇幅的限制,本文不对验证进行讲解,如果读者感兴趣,可以到文档起始位置提供的原文档连接进行查询(验证实际很简单,但描述比较麻烦,因此略去)。

2.2.5 schema的时间日期类型

Schema的时间日期类型与java的时间日期类型的转换比较繁琐,我们通过一个例子来了解对日期类型的转换(其它的,包括time类型和dateTime类型的原理相同)。现在我们将schema1-3的学生例子略加修改,添加一个学生生日的属性:

Schema2-2-5:student2_2_5.xsd

同样使用xjc编译这个schema,之后会生成Student类。我们发现生成的Student 类中的生日是如下类型:

@XmlElement(required = true)

@XmlSchemaType(name = "date")

protected XMLGregorianCalendar birthday;

我们将使用如下的方法,来设置XML的时间日期:

Code2-2-5:ProcessXML.java

public class ProcessXML {

public static void main(String[] args) throws DatatypeConfigurationException, JAXBException {

ObjectFactory of = new ObjectFactory();

Student student = of.createStudent();

DatatypeFactory df = DatatypeFactory.newInstance();//类型工厂,负责XML类型和java中使用类型的转换

//这里创建一个日期并赋值给student

student.setBirthday(df.newXMLGregorianCalendarDate(1985, 10, 12, DatatypeConstants.FIELD_UNDEFINED));

student.setName("Mary");

student.setId(5566);

File file = new File("student2_2_5.xml");

JAXBContext context =

JAXBContext.newInstance(ProcessXML.class.getPackage().getName());

context.createMarshaller().marshal(student, file);

Student newStudent = (Student)

context.createUnmarshaller().unmarshal(file);

System.out.println(newStudent.getBirthday());

}

}

从Code2-2-5中我们可以看出,需要使用一个DatatypeFactory来辅助我们创建日期时间。

生成的XML文档:

Xml2-2-5:student2_2_5.xml

Mary

1985-10-12

注意:通过Code2-2-5生成的XML文档将会拍在一行,可以设置marshaller的属性让其自动缩进(参考code1-3-3),但是如果在真正使用时,一般不需要提供自动缩进(自动缩进会占用系统资源,并且影响XML文档的生成速度,自动缩进仅对人分析XML文档有帮助,对应计算机来说没有任何意义)。

2.2.7 Schema的list类型

Schema的list会将一个java集合(必须是简单类型,例如string、int等)压缩成一个字符串进行存储。下面给出一个用来存储所有同学名字的schema。Schema2-2-7: students2_2_7.xsd

通过xjc生成类后,我们采用如下测试代码:

Code2_2_7: ProcessXML.java

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();

Students students = of.createStudents();

students.getAllStudents().add("Mary");

students.getAllStudents().add("Lily");

students.getAllStudents().add("Lucy");

File file = new File("students2_2_7.xml");

JAXBContext context = JAXBContext.newInstance(Students.class);

context.createMarshaller().marshal(students, file);

Students newStudents = (Students)

context.createUnmarshaller().unmarshal(file);

for(String str : newStudents.getAllStudents())

System.out.println(str);

}

生成的xml文档为:

Xml2-2-7 students2_2_7.xml

Mary Lily Lucy

注意schema的list类型会被生成一个数组,如下:

@XmlList

@XmlElement(required = true)

protected List allStudents;

因此通过向数组添加元素,来增加list的内容,不过需要注意的是,list通过空格来区分元素的,如果您单个元素内有空格,那么在解组的时候,可能会看到您不期望看到的结果。因此在编组前您可能更希望将单个元素内部的空格用其他符号进行替换。

2.2.9 使用枚举类型

枚举类型是在java和xml中都比较常用的类型。现在我们向student中添加一个性别的枚举,这个枚举可能有两种情况,一种情况是boy,另外一种情况是girl。

Xsd 2-2-9:student2_2_9.xsd

在这个xsd定义中,我们使用了xs作为标准XSD定义的前缀。

之后通过使用xjc编译生成java类,此时可以看到枚举类SexType生成了一个单独的类(一般的simpleType不会单独成类,这里很特殊),产生的定义如下:@XmlEnumValue("boy")

BOY("boy"),

@XmlEnumValue("girl")

GIRL("girl");

private final String value;

下面给出测试代码。

Code2-2-9 :ProcessXML.java

public class ProcessXML {

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();

Student student = of.createStudent();

student.setName("Mary");

student.setSex(SexType.GIRL);

File file = new File("student2_2_9.xml");

JAXBContext context = JAXBContext.newInstance(Student.class);

context.createMarshaller().marshal(student, file);

Student newStudent = (Student)

context.createUnmarshaller().unmarshal(file);

System.out.println(newStudent.getSex());

}

}

最后生成的XML文件为:

Xml2-2-9:student2_2_9.xml

Mary

girl

2.2.12.4 使用schema的choice

Schema中的choice选项提供了多选一的功能。比如,现在我们要在学生信息中添加一个联系方式,这个联系方式可以是手机或者固定电话,此时可以使用choice。但是,在java和XML转换的过程中,java并不保证联系方式中仅提供了一种,如果您要求仅提供一种联系方式,那么就需要对xml进行验证。

Xsd2-2-12-4:student2-2-12-4.xsd

通过xjc生成的Phone类中的成员是这样的:

protected String cellphone;

protected String telephone;

就是说,java将会提供两个变量,而不是一个。Java无法保证xml文档的选择性(即有且仅有一个被选中)。

测试代码测试了我们描述的情况。

Code2-2-12-4:ProcessXML.java

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();

Student student = of.createStudent();

student.setName("Mary");

Phone phone = of.createPhone();

//在这里,我们赋值了两种联系方式

phone.setCellphone("138********");

phone.setTelephone("0467-5643247");

student.setPhone(phone);

File file = new File("student2_2_12_4.xml");

JAXBContext context = JAXBContext.newInstance(Student.class);

context.createMarshaller().marshal(student, file);

Student newStudent = (Student)

context.createUnmarshaller().unmarshal(file);

System.out.println(newStudent.getPhone().getCellphone());

System.out.println(newStudent.getPhone().getTelephone());

}

执行后的结果。

Xml2-2-12-4:student2_2_12_4.xml

Mary

138********

0467-5643247

可以看出java并不能保证schema的choice语义。同时java也不能保证union的语义,因此,我们尽量避免使用union。(java中没有与union相对的类型)

2.2.12.5 同构的集合

同种类型的集合也是最常用的集合。我们现在需要定义一个clazz(班级),这个班级里包含了很多的学生。这样班级中有一个集合就是由学生构成的。

Xsd2-2-12-5:clazz2_2_12_5.xsd

maxOccurs="100"minOccurs="0"/>

通过使用maxOccurs和minOccurs来限定集合的大小,需要注意的是,默认情况下,java不会提供对限定的验证,如果需要验证,则需要提供附加的验证逻辑。

通过xjc生成java类后,可以看到Clazz类中有一个数组:

protected List students;

之后可以通过下面测试代码查看生成xml。

Code2-2-12-5:ProcessXML.java

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();

Clazz clazz = of.createClazz();

clazz.getStudents().add(of.createStudent("Mary"));

clazz.getStudents().add(of.createStudent("Lucy"));

clazz.getStudents().add(of.createStudent("Lury"));

File file = new File("clazz2_2_12_5.xml");

JAXBContext context = JAXBContext.newInstance(Clazz.class);

context.createMarshaller().marshal(clazz, file);

Clazz newClazz = (Clazz)

context.createUnmarshaller().unmarshal(file);

for(Student s : newClazz.getStudents())

System.out.println(s.getName());

}

这里需要注意的是,我们在of工厂中自己编写一个带参数的构造方法createStudent,方便我们使用。

生成的xml文档内容为:

Xml2-2-12-5:clazz2_2_12_5.xml

Mary

Lucy

Lury

从生成的结果来看,有些令人费解,首先,students是集合的名字,而在这里却用来包装了student。不清楚这是不是bug(有可能是我的思路有问题,如果谁有独到见解,请给予及时评论)。对应JAXB来说,处理XML文档时如果同一个元素下的多个子元素具有同一的标签名,那么JAXB认为这个标签名就是一个集合的名字,会将所有元素解组到一个集合中。可以使用包装方式,参考https://www.sodocs.net/doc/ef9281446.html,/tutorial/section_2_3_5-Spurious-Classes.html#Spurious Classes

2.2.13 schema的继承(一个十分复杂的例子)

OO的设计思想是java的核心思想,但并不是XML和schema的设计思想,但是Schema提供了一些特性可以支持OO的部分特性,通过schema描述的定义,也可以反演出java类的继承关系。假设有这样一个图形学的设计,需要我们设计图元,图元包括点、线和圆。根据OO的设计思想,我们将图元设计为父类,而点、线和圆是图元的子类,同时我们也设计一个集合类,用于存放多个图元,并且这个集合类也继承于图元类(这实际描述的是一种叫组合模式的设计模式)类间关系如下:

1

Xsd2-2-13 :graphic2-2-13.xsd

type="graphicCollection"/>

注意schema2-2-13的定义中,仅定义了类型,并没有定义根元素,因为在我们类的OO设计中并没有根元素,所以,要注意如何将一个类包装成根元素,code2-2-13-1给出了这种包装方式。

public JAXBElement createGraphicCollection(String ns,String tag,GraphicCollection gc)

{

QName qname = new QName(ns, tag);

return new

JAXBElement(qname,GraphicCollection.class,gc);

}

这段代码会将一个GraphicCollection对象包装成JAXBElement类型,因此可以做为根元素使用。

通过过xjc可以生成相应的类和类的继承关系。

例如,生成的其中一个类的声明如下:

public class Circle

extends Graphic

可以使用下面的方法完成测试:

Code2-2-13 :ProcessXMl.java

public class ProcessXML {

public static void outGraphics(GraphicCollection gc)//用于输出GraphicCollection中的图元信息

{

for(Graphic g :gc.getLineOrCricleOrPoint())//这里需要判断图元的具

体类别查看注解2

{

if(g instanceof Line)

System.out.println(String.format("line : begin point %d %d end point %d %d", new Object[]{((Line) g).getBeginPoint().getX(),

((Line) g).getBeginPoint().getY(),((Line)

g).getEndPoint().getX(),((Line) g).getEndPoint().getY()}));

else if(g instanceof Circle)

System.out.println(String.format("circle : base

point %d %d radius %d", new Object[]{((Circle) g).getBasePoint().getX()

,((Circle) g).getBasePoint().getY(),((Circle) g).getRadius()}));

else if(g instanceof Point)

System.out.println(String.format("point : %d %d", new Object[]{((Point) g).getX(),((Point) g).getY()}));

else if(g instanceof GraphicCollection)

outGraphics((GraphicCollection) g);//这里是递归调用}

}

public static void main(String[] args) throws JAXBException { ObjectFactory of = new ObjectFactory();

GraphicCollection gc = of.createGraphicCollection();//创建集合,向集合添加元素

gc.getLineOrCricleOrPoint().add(of.createPoint(100, 10));

gc.getLineOrCricleOrPoint().add(of.createLine(of.createPoint(200, 12), of.createPoint(21, 42)));

gc.getLineOrCricleOrPoint().add(of.createCircle(of.createPoint(10 0, 10), 50));

GraphicCollection gc2 = of.createGraphicCollection();//又创建了

一个集合

gc2.getLineOrCricleOrPoint().add(gc);//将原有集合添加到本集合中(这就类似多叉树结构)

gc2.getLineOrCricleOrPoint().add(of.createPoint(10, 10));//此外又添加一个新元素

File file = new File("graphic2_2_13.xml");//定义输出文件

JAXBContext context =

JAXBContext.newInstance(GraphicCollection.class);//设置环境

Marshaller marshaller = context.createMarshaller();

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

marshaller.marshal(of.createGraphicCollection("", "graphicCollection", gc2), file);//编组

//解组,注意解组使用了两个参数,第一个是数据来源,第二个是类型查看注解1

JAXBElement gcElement =

context.createUnmarshaller().unmarshal(new

StreamSource(file),GraphicCollection.class);

outGraphics(gcElement.getValue());//输出信息

}

}

注解1:由于在xsd 2-2-13中没有定义根元素,因此我们使用了包装来创建一个根元素,在包装过程中第二个参数就是根元素的标签名字。在解组的时候,JAXB 无法获悉编组时提供的根元素与具体哪个类进行映射,所以在解组时需要提供一个额外的关于类的信息,此时JAXB直接使用这个类进行解组,并且会忽略掉根标签(根标签的实际作用就是使JAXB能够确定使用哪个类进行解组)。

注解2:由于类的继承关系,所以获取到的图元集合都是所有图元父类的对象,这时需要向下转型,才能分辨出具体的图元。

生成的xml文档如下:

Xml 2-2-13 : graphic2_2_13.xml

100

10

200

12

21

42

100

10

50

10

10

输出的内容如下:

point : 100 10

line : begin point 200 12 end point 21 42

circle : base point 100 10 radius 50

point : 10 10

这个例子很复杂,不论是从java类间的关系,对schema的理解和对编组解组的理解都很重要,希望大家好好消化。

如果将xsd2-2-13修改变成xsd2-2-13-2的形式:

Xsd2-2-13-2 : graphic2_2_13_2.xsd

使用基本上与2-2-13相同的代码,则得到如下xml:

100

相关主题