由于wsgen工具在底层使用了JAX-B相关的API包来实现Java与XML之间的类型转换,所以让我们通过一个JAX-B例子来了解一下Java与XML之间是如何转换的。我们创建一个Person类(人)与一个Skier类(滑雪运动员),在每个类定义的起始处都有一个单独的注解,用来说明Java到XML的绑定。Person类被注解为一个@XmlType,而Skier类被注解为一个@XmlRootElement。如下例:
Person.java类:
package ch04.ts;
import javax.xml.bind.annotation.XmlType;
@XmlType
public class Person {
private String name;
private int age;
private String gender;
public Person() {}
public Person(String name,int age,String gender){
setName(name);
setAge(age);
setGender(gender);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
Skier.java类:
package ch04.ts;
import java.util.Collection;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Skier {
private Person person;
private String national_team;
private Collection<String> major_achievements;
public Skier(){}
public Skier(Person person,String national_team,
Collection<String> major_achievements){
setPerson(person);
setNational_team(national_team);
setMajor_achievements(major_achievements);
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public String getNational_team() {
return national_team;
}
public void setNational_team(String national_team) {
this.national_team = national_team;
}
public Collection<String> getMajor_achievements() {
return major_achievements;
}
public void setMajor_achievements(Collection<String> major_achievements) {
this.major_achievements = major_achievements;
}
}
@XmlType与@XmlRootElement注解干预了Skier对象的编码(Marshaling),编码是指将一个内存对象(比如一个Skier对象)以一个XML文档形式进行处理的过程,所以,比如说利用网络传输的编码在接收的一端可以被解码(Unmarshal)处理。在通常使用中,编码和解码之间无大的区别,大致等同于序列化/反序列化之间的区别。JAX-B支持将内存中的java对象序列化为XML文档,同时将XML文档反序列化为内存中的java对象。
在Person类中,注解@XmlType表明,JAX-B应该为该java类型产生一个对应XML模式类型。在Skier类中,注解@XmlRootElement表明,JAX-B应该为该Java类产生一个XML文档(最外层的或根元素)。于是,最终产生的XML文档的最外层的根元素用来描述一个skier,同时在skier中有一个嵌套的子元素描述一个person。
对skier进行编码与解码的示例Marshal.java:
package ch04.ts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Marshal {
private static final String file_name = "bd.xml";
public static void main(String[] args) {
new Marshal().run_example();
}
private void run_example(){
try {
//创建JAXB上下文对象
JAXBContext ctx = JAXBContext.newInstance(Skier.class);
//创建编码器
Marshaller m = ctx.createMarshaller();
//设置为格式化输出
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Skier skier = createSkier();
//把对象编码为XML数据使用屏幕输出
m.marshal(skier, System.out);
FileOutputStream out = new FileOutputStream(file_name);
//把对象编码为XML数据输出到文件
m.marshal(skier, out);
out.close();
//创建解码器
Unmarshaller u = ctx.createUnmarshaller();
//把刚才输出到XML的数据再解码为java对象
Skier bd_clone = (Skier) u.unmarshal(new File(file_name));
System.out.println();
//再把对象编码为XML数据输出到屏幕
m.marshal(bd_clone, System.out);
} catch (JAXBException e1) {
e1.printStackTrace();
} catch (FileNotFoundException e2){
e2.printStackTrace();
} catch (IOException e3){
e3.printStackTrace();
}
}
private Skier createSkier(){
Person bd = new Person("Bjoern Daehlie",41,"Male");
List<String> list = new ArrayList<String>();
list.add("12 Olympic Medals");
list.add("9 World Championships");
list.add("Winningest Winter Olympian");
list.add("Greatest Nordic Skier");
return new Skier(bd,"Norway",list);
}
}
默认情况下,JAX-B编码遵循标准的Java和JavaBean命名规范。比如Skier类的XML标签名称为skier,Person类的XML标签名称是person。针对Skier对象和封装的Person对象,通过调用JavaBean风格的get方法获取对象的相关状态信息,输入到要生成的XML文档中。
遵循JavaBean命名规范的类进行编码和解码时,可以对添加了注解的方法进行重写。然而,一个由wsgen生成的java类并不遵循JavaBean命名规则,请看下面例子:
@XmlRootElement(name = "getTimeAsElapsedResponse",namespace = "http://ts.ch02/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getTimeAsElapsedResponse",namespace = "http://ts.ch02/")
public class GetTimeAsElapsedResponse{
@XmlElement(name = "return", namespace = "")
private long _return;
public long get_return(){
return this._return;
}
public void set_return(long _return){
this._return = _return;
}
}
注意@XmlAccessorType(XmlAccessType.FIELD)这个注解,它指出字段名称“_return”将被编码和解码,而并没有遵循JavaBean命名规范来定义对应的getter/setter方法对。
另外,通过设定注解属性的其他值,可以覆盖默认的命名约定,如果Skier类中的注解声明修改成如下形式:
@XmlRootElement(name = "NordicSkier")
那么生成的XML文档将会变成:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<NordicSkier>
.....
注意:JAX-B解码要求每一个类必须拥有一个公开的无参构造方法,用来构造对象。