过往的项目中数据存储都离不开数据库,不过最近做的一个项目的某些数据(比如人员信息、菜单、权限等等)却完全没有涉及任何数据库操作,直接XML搞定。这里无意比较优劣,因为数据库存储和XML存储本就有不同的适用场景,盲目比较毫无意义,只是因为业务需要,仅此而已。先来概念一下——XML,可扩展标记语言,设计宗旨是用来传输数据而非显示数据,其遵循W3C标准,是一种通用的数据交换格式,具有很强的跨平台性,并且数据无需转换,所以,如果你要将数据做跨平台传输,那么把数据保存在 XML 文件中是有好处的。当然,这里要说明,由于XML仅仅是作为一种文档模式的结构化存储,所以并不适用于大数据量的存储。现在的Java中有很多类库比如DOM、SAX、JDOM和DOM4J等等都可以操作XML,但如果仅仅是想做JavaBean和XML节点元素的互相转换,而不涉及动态XML的处理,那么JAXB绝对是一个不错的选择。在比较新的jdk版本中,JAXB都是jdk的扩展包javax中自带的类库,不需要你引入第三方jar包。下面,博主正式给看客上菜,详细介绍一下JAXB的实际用法——

一 JavaBean和XML相互转换初体验

 package model;

 import javax.xml.bind.annotation.*;
import java.io.Serializable; //JavaBean代码 @XmlType(propOrder = {})
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable { private String userName;
private int age;
private String role;
private String bibi; public User() {
} public User(String userName, int age, String role, String bibi) {
this.userName = userName;
this.age = age;
this.role = role;
this.bibi = bibi;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @XmlAttribute
public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @XmlElement
public String getRole() {
return role;
} public void setRole(String role) {
this.role = role;
} @XmlTransient
public String getBibi() {
return bibi;
} public void setBibi(String bibi) {
this.bibi = bibi;
} @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
", role='" + role + '\'' +
", bibi='" + bibi + '\'' +
'}';
}
} //测试
public class test {
@Test
public void saveXmlTest() {
User user = new User("陈本布衣", 2018, "超级管理员","瞎哔哔");
File file = new File("E://user.xml");
try {
JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
Marshaller marshaller = jaxbContext.createMarshaller();
//格式化输出,即按标签自动换行,否则就是一行输出
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//设置编码(默认编码就是utf-8)
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
//是否省略xml头信息,默认不省略(false)
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
marshaller.marshal(user, file);
} catch (JAXBException e) {
e.printStackTrace();
}
} @Test
public void getUserTest() {
File file = new File("E://user.xml");
try {
JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
User user = (User) unmarshaller.unmarshal(file);
System.out.println(user.toString());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}

生成的XML:

二 JAXB使用基础介绍

  ① 常用API

  • JAXBContext类,是应用的入口,通过该类创建序列化和反序列化对象,也即编组对象和解组对象;
  • Marshaller 编组接口,将Java对象序列化为XML数据;
  • Unmarshaller 解组接口,将XML数据反序列化为Java对象。

  ② 常用注解

  • @XmlRootElement,将Java类或枚举映射成XML元素根节点,是唯一一个必须注解,name属性指定根节点名称,不指定默认为类名的小写;
  • @XmlElement,将Java类的一个属性映射为XML节点元素,name属性可自定义元素名;
  • @XmlAttribute,将Java类的一个属性映射为XML节点元素的属性,name属性可自定义属性名;
  • @XmlType,将Java类或枚举类型映射到XML模式类型,常与@XmlRootElement、@XmlAccessorType共用,propOrder属性定义字段生成的XML节点顺序;
  • @XmlAccessorType,控制字段或属性的序列化。属性XmlAccessType有4个常量值:FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标注)字段到XML;PROPERTY表示java对象中所有通过getter/setter方式绑定成属性到XML;PUBLIC_MEMBER表示Java对象中所有的public访问权限的成员变量和通过getter/setter方式访问的成员变量,该值为默认值;NONE表示Java对象的所有属性都不映射为XML的元素;
  • @XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序,有两个属性,AccessorOrder.ALPHABETICAL——对生成的XML元素按字母书序排序,XmlAccessOrder.UNDEFINED——不排序,默认为该值;
  • @XmlJavaTypeAdapter,自定义适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),解决日期(Date),数字(Number)格式化问题;
  • @XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器),该注解只能用在集合上;
  • @XmlTransient ,用于标示在由Java对象映射XML时,忽略此属性,在生成的XML文件中将不出现此元素。

  ③ 实际应用中注意的问题

  ① 如果JavaBean中定义了有参的构造器,那么必须同时定义无参构造器,否则转XML会抛无默认构造函数的异常;

  ② 成员变量值为NULL时,将不会映射成对应的XML元素——由于基本数据类型默认值不为空,所以基本数据类型不设值也会映射成XML元素,值为默认值,所以如果模型需要基本数据,在属性定义的时候尽量使用包装类型;

  ③ @XmlAccessorType 注解中如果属性值为XmlAccessType.FIELD,则表示通过成员变量来映射,set/get方法上的映射注解就是多余的,所以如果此时set/get方法上再标注元素或者属性映射注解,将抛属性重复性异常;属性值为XmlAccessType.NONE不映射为XML元素的前提是Java字段或set/get方法上都没有映射注解;

  ④ @XmlType propOrder属性能够自定义字段的排序,该属性如果设置,要么写成{}的形式,否则在就必须将所有@XmlElement标注或者没有@XmlElement标注的但实际上会被映射为XML节点的字段添加到排序列表,不然会抛异常;如果propOrder属性设置有值,@XmlAccessorOrder注解的元素排序规则将失效;

三 应用实际

  先准备好测试用的工具方法:

 package util;

 import model.User;

 import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.File; public class JaxbUtil { public static void convertToXml(Object obj, File file) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
Marshaller marshaller = jaxbContext.createMarshaller();
//格式化输出,即按标签自动换行,否则就是一行输出
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//设置编码(默认编码就是utf-8)
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
//是否省略xml头信息,默认不省略(false)
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
marshaller.marshal(obj, file);
//控制台输出
marshaller.marshal(obj,System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
} public static <T> T convertToJavaBean(Class<T> clz, File file) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(clz);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
T t = (T) unmarshaller.unmarshal(file);
return t;
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
}

  ① 简单对象处理

  简单对象处理起来比较简单,譬如人员对象User中包含菜单Menu,只需将定义的普通Menu对象也按照JAXB的注解进行标注,在User对象中当成普通字段一样的定义即可——

@XmlType(propOrder = {"userName","role","menu"})
@XmlRootElement(name = "user")
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable { private String userName;
private int age;
private String role;
private String bibi;
private Menu menu;
public User() {
} public User(String userName, int age, String role, String bibi) {
this.userName = userName;
this.role = role;
this.age = age;
this.bibi = bibi;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @XmlAttribute
public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @XmlElement(nillable=true)
public String getRole() {
return role;
} public void setRole(String role) {
this.role = role;
} @XmlTransient
public String getBibi() {
return bibi;
} public void setBibi(String bibi) {
this.bibi = bibi;
} @XmlElement
public Menu getMenu() {
return menu;
} public void setMenu(Menu menu) {
this.menu = menu;
} @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
", role='" + role + '\'' +
", menu=" + menu +
'}';
}
} //菜单对象 @XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Menu {
private String name;
private String id; public Menu() {
} public Menu(String name, String id) {
this.name = name;
this.id = id;
} @Override
public String toString() {
return "Menu{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}

 

  ② 集合处理

  实际应用场景中集合应用要更常见一些,比如上面的用户菜单,一个用户肯定会有多个不同的菜单,所以,我们来将上面的菜单改用集合处理——

 package model;

 import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.util.Date;
import java.util.List; @XmlType(propOrder = {"userName", "role", "menus"})
@XmlRootElement(name = "user")
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable { private String userName;
private int age;
private String role;
private String bibi;
private List<Menu> menus; public User() {
} public User(String userName, int age, String role, String bibi) {
this.userName = userName;
this.role = role;
this.age = age;
this.bibi = bibi;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @XmlAttribute
public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @XmlElement
public String getRole() {
return role;
} public void setRole(String role) {
this.role = role;
} @XmlTransient
public String getBibi() {
return bibi;
} public void setBibi(String bibi) {
this.bibi = bibi;
} @XmlElement
public List<Menu> getMenus() {
return menus;
} public void setMenus(List<Menu> menus) {
this.menus = menus;
} @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
", role='" + role + '\'' +
", menus=" + menus +
'}';
}
}
 package model;

 import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List; @XmlRootElement
public class Menu {
private String name;
private String id;
private List<Menu> child; public Menu() {
} public Menu(String name, String id) {
this.name = name;
this.id = id;
}
@XmlAttribute
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
@XmlAttribute
public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public List<Menu> getChild() {
return child;
} public void setChild(List<Menu> child) {
this.child = child;
} @Override
public String toString() {
return "Menu{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
'}';
}
}
 package test;

 import model.Menu;
import model.User;
import org.junit.Test;
import util.JaxbUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.List; public class test {
@Test
public void saveXmlTest() {
User user = new User("陈本布衣", 2018, "超级管理员","瞎哔哔");
List<Menu> list1 = new ArrayList<>();
Menu menu1 = new Menu("系统管理","9527");
Menu child1 = new Menu("权限管理","9999");
Menu child2 = new Menu("用户管理","2322");
list1.add(child1);
list1.add(child2);
menu1.setChild(list1);
List<Menu> list2 = new ArrayList<>();
Menu menu2 = new Menu("参数配置","2222");
Menu child3 = new Menu("权限管理","3333");
Menu child4 = new Menu("用户管理","4444");
list2.add(child3);
list2.add(child4);
menu2.setChild(list2);
List<Menu> menus = new ArrayList<>();
menus.add(menu1);
menus.add(menu2);
user.setMenus(menus);
File file = new File("E://user.xml");
JaxbUtil.convertToXml(user,file);
} @Test
public void getUserTest() {
File file = new File("E://user.xml");
User user = JaxbUtil.convertToJavaBean(User.class, file);
System.out.println(user);
}
}

 

  上面的菜单中似乎少了点层次关系,这个时候可以使用集合包装器注解@XmlElementWrapper自定义一个包装节点,这样产生的XML文档才更有层次:

     @XmlElementWrapper(name = "menu")
@XmlElement
public List<Menu> getMenus() {
return menus;
}

  最终产生的XML文档就是这样的:

 

  ③ 格式化处理

  业务数据中日期、数值通常是必不可少的,在数据存储的时候,这些数据通常都需要做格式化处理,比如将日期格式化,货币型数值处理等等。JAXB中格式化处理需要继承适配器抽象类XmlAdapter,并覆写其序列化和反序列化的方法,这里仅用常用的日期格式化为例:

 package adapter;

 import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date; public class DateAdapter extends XmlAdapter<String, Date> {
private static final DateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override
public Date unmarshal(String date) throws Exception {
return SDF.parse(date);
} @Override
public String marshal(Date date) throws Exception {
return SDF.format(date);
}
}

  将该适配器通过注解应用到User类表时间的date字段上:

     @XmlJavaTypeAdapter(DateAdapter.class)
public Date getDate() {
return date;
} public void setDate(Date date) {
this.date = date;
}

  最后的时间就是按照格式化输出——

  

  ④ 数据的修改

  由于XML是文档数据类型,对于文档数据的修改操作,通常采用的都是先将文本内容全部读取到内存,修改完成后再写回去文本的方式——虽然Java中有RandomAccessFile类可以实现对文本任意位置的访问修改,但博主以为,在JAXB这种对象模型映射成XML的业务中并不适用。我们将上面的模型稍微简化一下,完成根据用户id修改用户数据的测试——

@XmlType(propOrder = {"users", "userName", "role", "remark"})
@XmlRootElement(name = "user")
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User implements Serializable { private String userName;
private Integer id;
private String role;
private String remark;
private List<User> users; public User() {
} public User(String userName, Integer id, String role, String remark) {
this.userName = userName;
this.id = id;
this.role = role;
this.remark = remark;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} @XmlAttribute
public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} @XmlElement
public String getRole() {
return role;
} public void setRole(String role) {
this.role = role;
} public String getRemark() {
return remark;
} public void setRemark(String remark) {
this.remark = remark;
} public List<User> getUsers() {
return users;
} public void setUsers(List<User> users) {
this.users = users;
}
} //测试代码
public class test { @Test
public void editUser() {
User root = new User();
File file = new File("E://user.xml");
//模拟修改后的一条数据
User editUser = new User("陈本布衣", 2018, "超级管理员","数据修改后");
User user = JaxbUtil.convertToJavaBean(User.class, file);
List<User> users = user.getUsers();
for (int i = 0; i < users.size(); i++) {
if(users.get(i).getId().equals(editUser.getId())){
users.set(i,editUser);
}
}
root.setUsers(users);
JaxbUtil.convertToXml(root,file);
}
}

  最后,在XML文档中你可以看到文档内容已经被修改了——

   

四 问题补充

  上述博文中描述的工具方法仅仅是出于学习中追根问本的目的写得稍微冗余了些,实际上,我所知道的是最迟从jdk1.7开始,JAXB就对解组和编组的方法进行了更简单的封装,所以,实际项目中除非自己要进行个性化设置,否则大可不用自己再创建JAXBContext实例,直接通过JAXB静态调用相应的工具方法就行了,有兴趣的看官稍微跟踪一下源码就能了然,于是上面的工具方法可以写得更简单——

 package util;

 import javax.xml.bind.JAXB;
import java.io.File; public class JaxbUtil { public static void convertToXml(Object obj, File file) {
JAXB.marshal(obj,file);
} public static <T> T convertToJavaBean(Class<T> clz, File file) {
return JAXB.unmarshal(file, clz);
}
}

  OK,对于JAXB的知识分享就差不多这么些了。对于这种比较单一技能点的学习,就是根据API多写点代码练习测试,从测试的结果对错中总结出自己的深层理解,并在实际项目学以致用,不变应万变,望看官读毕都有所收获!

JAXB应用实例的更多相关文章

  1. XML学习记录1-复习SAX,DOM和JAXB

    对xml文档的解析常见的有JDK中的sax,dom,jaxb,stax和JAVA类库JDOM和DOM4J,下面先说说前三个. Java中解析XML的工具很多,像JDOM,DOM4J等,但Java标准库 ...

  2. JAXB - java xml解析

    常用API JAXBContext类,是应用的入口,通过该类创建序列化和反序列化对象,也即编组对象和解组对象: Marshaller 编组接口,将Java对象序列化为XML数据: Unmarshall ...

  3. Java_数据交换_JAXB_用法入门

    一.前言 最近有个需求,需要进行xml 与 bean 的相互转化. 使用 JAXB 可完成这个需求. 二.概述 JAXB(Java Architecture for XML Binding) 是一个业 ...

  4. 最近学习工作流 推荐一个activiti 的教程文档

    全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...

  5. 完整记录一则Oracle 11.2.0.4单实例打PSU补丁的过程

    本文记录了打PSU的全过程,意在体会数据库打PSU补丁的整个过程. 1.OPatch替换为最新版本2.数据库软件应用19121551补丁程序3.数据库应用补丁4.验证PSU补丁是否应用成功 1.OPa ...

  6. java生成解析xml的另外两种方法JAXB

     JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例文档反 ...

  7. JAX-WS:背后的技术JAXB及传递Map

    转载:http://www.programgo.com/article/98912703200/ 1.什么是JAX-WS JAX-WS (JavaTM API for XML-Based Web Se ...

  8. JAXB注解【转】

    http://blog.csdn.net/lw371496536/article/details/6942045 JAXB(Java API for XML Binding),提供了一个快速便捷的方式 ...

  9. CXF整合Spring发布WebService实例

    一.说明: 上一篇简单介绍了CXF以及如何使用CXF来发布一个简单的WebService服务,并且介绍了客户端的调用. 这一篇介绍如何使用CXF与spring在Web项目中来发布WebService服 ...

随机推荐

  1. zTree多条件模糊查询

    function searchFun() { var zTrees=$.fn.zTree.getZTreeObj("ztree");//获得所有几点 var hiddenNodes ...

  2. JavaScript 中 apply 、call 的详解

    apply 和 call 的区别 ECMAScript 规范给所有函数都定义了 call 与 apply 两个方法,它们的应用非常广泛,它们的作用也是一模一样,只是传参的形式有区别而已. 原文作者:林 ...

  3. android 串口开发第二篇:利用jni实现android和串口通信

    一:串口通信简介 由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以 ...

  4. C语言_第二讲_规范以及常用数据类型

    一丶编码规范基本数据类型 编码规范 任何程序员,都应该有良好的的编码习惯,便于以后的代码可读性和维护 常见了编码规范有 匈牙利命名法 驼峰式大小写 匈牙利命名法: 是电脑程序设计中的一种变量命名规则, ...

  5. 尝试在条件“$(_DeviceSdkVersion) >= 21”中对计算结果为“”而不是数字的“$(_DeviceSdkVersion)

    晚上搞xamarin ,运行xamarin项目好好的,不知道怎么回事,一次运行xamarin android项目的时候,部署失败,以前也是遇到这样的错误. 尝试在条件"$(_DeviceSd ...

  6. Who's in the Middle

    FJ is surveying his herd to find the most average cow. He wants to know how much milk this 'median' ...

  7. bzoj 4813: [Cqoi2017]小Q的棋盘

    Description 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能 在有连线的格点之间移动.整个棋盘上共有V个格点,编号为0,1,2-,V- ...

  8. bat常用命令

    1.@它的作用是隐藏它后面这一行的命令本身(只能影响当前行).2.echo中文为"反馈"."回显"的意思.它其实是一个开关命令,就是说它只有两种状态:打开和关闭 ...

  9. Spring 自动装配及自动注册的相关配置

    Spring支持好几种自动装配(Autowiring)的方式,以及自动扫描并注册Bean的配置(在beans.xml中配置). 下文我们进行一个小结. 1. <context: annotati ...

  10. thinkphp3.2.3的使用心得(零)

    从模板传参到控制器 模板中代码: <volist name="list" id="vo"> <a href="__CONTROLLE ...