声明:此文为原创,转载请声明出处!
最近在项目中用到了JAVABean和XML的相互转化,由于花了比较多的时间和精力去实现,所以记录一下是如何实现的。我用到的JAVABean转xml有两种实现办法,第一种是利用工具类直接转化,第二种是先将JAVABean转换成JSON,然后再转成XML。我的项目是用springboot搭建的,jdk是1.8的, maven版本是3.6.0。
第一种:利用工具类将JAVABean直接转成XML
首先创建一个工具类,类里面有两种方法,xml转JAVABean和JAVABean转xml,网上工具类有很多,这个大家自己选择,有这两种方法就可以。
/**
* 将对象转为对应的xml报文
*
* @param object 对象
* @param classT 类
* @return xml
* @throws JAXBException ex
*/
public static String objectToXml(Object object, Class<?>... classT) throws JAXBException {
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(classT);
Marshaller marshaller = context.createMarshaller();
//设置编码格式
marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
marshaller.marshal(object, writer);
return new String(writer.getBuffer());
}
/**
* 将xml报文转换为对象
*
* @param clazz clazz
* @param xmlStr xml
* @return object
*/
public static Object xmlToObject(String xmlStr, Class<?>... clazz) {
Object xmlObject = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader sr = new StringReader(xmlStr);
xmlObject = unmarshaller.unmarshal(sr);
} catch (JAXBException e) {
log.error("xmlStrToObject error:", e);
}
return xmlObject;
}
这个工具类我引了一个lombok用来打印日志 不用的可以去掉
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
接下来再创建一个实体类
@XmlRootElement(name = "Student")
public class Student {
@XmlElement(name = "Name")
private String name;
@XmlElement(name = "Sex")
private String sex;
@XmlTransient
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@XmlTransient
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
其中主要起作用的是@XmlRootElement(name = "Student")
、@XmlElement(name = "Name")
和@XmlTransient
这三个注解,其中@XmlRootElement(name = "Student")
这个注解是必须要用的,用在类名上,name属性是用来设置xml根节点的名字,如果没有的话,就默认是全小写的类名。@XmlElement(name = "Name")
和@XmlTransient
是搭配用的,要么都不用,要么都用,不能缺一个,不然使用工具类转化的时候会报错。
接下来创建一个测试方法
public String jsonToXml() throws Exception{
Student student = new Student();
student.setName("张三");
student.setSex("男");
log.info("student:"+student.toString());
String xml = XmlUtil.objectToXml(student,Student.class);
log.info("xml:"+xml);
return xml;
}
XmlUtil.objectToXml(Object object, Class<?>… classT)这个方法,第一个参数是要转化的实体类,第二个参数是实体类的class数组
执行结果为:
student:Student{name='张三', sex='男'}
xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Student>
<Name>张三</Name>
<Sex>男</Sex>
</Student>
执行结果我格式化了一下 方便大家看。这样一个简单的JAVABean转xml就成功了,那项目实际会有更复杂的JAVABean,那再创建一个课程的实体类。
public class Course {
@XmlElement(name = "Id")
String id;
@XmlElement(name = "Score")
String score;
@XmlTransient
public String getId() { return id; }
public void setId(String id) { this.id = id; }
@XmlTransient
public String getScore() { return score; }
public void setScore(String score) { this.score = score; }
@Override
public String toString() {
return "Course{" +
"id='" + id + '\'' +
", score='" + score + '\'' +
'}';
}
}
然后把Course当做Student的一个属性,这时候Student就变成了下面这样:
@XmlRootElement(name = "Student")
public class Student {
@XmlElement(name = "Name")
private String name;
@XmlElement(name = "Sex")
private String sex;
@XmlElement(name = "Course")
private Course course ;
@XmlTransient
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@XmlTransient
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
@XmlTransient
public Course getCourse() { return course; }
public void setCourse(Course course) { this.course = course; }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", course=" + course.toString() +
'}';
}
}
测试类变成了下面这样:
public String jsonToXml() throws Exception{
Student student = new Student();
student.setName("张三");
student.setSex("男");
Course course = new Course();
course.setId("01");
course.setScore("100");
student.setCourse(course);
log.info("student:"+student.toString());
String xml = XmlUtil.objectToXml(student,Student.class);
log.info("xml:"+xml);
return xml;
}
执行结果如下:
student:Student{name='张三', sex='男', course=Course{id='01', score='100'}}
xml:<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Student>
<Name>张三</Name>
<Sex>男</Sex>
<Course>
<Id>01</Id>
<Score>100</Score>
</Course>
</Student>
有细心的同学可能发现了,Course这个类的类名上面没有@XmlRootElement
注解,但是还可以正常运行。这是因为当一个类作为另一个类的属性转XML时,就不需要@XmlRootElement
注解了,就算用了也不会生效,比如设置注解@XmlRootElement(name = "Course")
,但是转化后的XML节点名字还是course。这种情况生效的是Student类中course属性上面的@XmlElement(name = "Course")
注解了。
接下来用刚才转化后的XML来转回成JAVABean,测试代码如下:
public Student xmlToJson() throws Exception{
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Student>\n" +
" <Name>张三</Name>\n" +
" <Sex>男</Sex>\n" +
" <Course>\n" +
" <Id>01</Id>\n" +
" <Score>100</Score>\n" +
" </Course>\n" +
"</Student>";
log.info("xml:"+xml);
Student student = (Student)XmlUtil.xmlToObject(xml,Student.class);
log.info("student:"+student.toString());
return student;
}
执行结果如下:
xml:<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Student>
<Name>张三</Name>
<Sex>男</Sex>
<Course>
<Id>01</Id>
<Score>100</Score>
</Course>
</Student>
student:Student{name='张三', sex='男', course=Course{id='01', score='100'}}
这样一个简单的XML和JAVABean的相互转化就完成了。
那还有一种稍微复杂的情况就是,现在有一个MathCourse类,继承了Course类,然后作为Student的course属性,这样的话,之前的方法就不适用了。先来看看MathCourse类长什么样:
@XmlRootElement(name = "MathCourse")
public class MathCourse extends Course{
@XmlElement(name = "TeacherId")
private String teacherId;
@XmlTransient
public String getTeacherId() { return teacherId; }
public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
}
改完后的Student类:
@XmlRootElement(name = "Student")
public class Student {
@XmlElement(name = "Name")
private String name;
@XmlElement(name = "Sex")
private String sex;
private Course course ;
@XmlTransient
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@XmlTransient
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
@XmlElementRef
public Course getCourse() { return course; }
public void setCourse(Course course) { this.course = course; }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", course=" + course.toString() +
'}';
}
}
这个时候Student类就不能用@XmlElement(name = "Name")
和@XmlTransient
这一对注解了,要用@XmlElementRef
这个注解,如果不用注解或者还是用@XmlElement(name = "Name")
这一对注解的话,也不会报错,但是<course>会变成<course xsi:type=“mathCourse” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”>,所以改成@XmlElementRef
注解就正常了,有兴趣的同学可以去查查为什么。还有一点要注意的就是MathCourse类上面的@XmlRootElement(name = "MathCourse")
是有用的,测试类如下:
public String jsonToXml() throws Exception{
Student student = new Student();
student.setName("张三");
student.setSex("男");
MathCourse mathCourse = new MathCourse();
mathCourse.setTeacherId("123");
mathCourse.setId("02");
mathCourse.setScore("99");
student.setCourse(mathCourse);
//course的实际子类类名要作为参数传给方法
String xml = XmlUtil.objectToXml(student,Student.class,MathCourse.class);
log.info("xml:"+xml);
return xml;
}
执行结果如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Student>
<Name>张三</Name>
<Sex>男</Sex>
<MathCourse>
<Id>02</Id>
<Score>99</Score>
<TeacherId>123</TeacherId>
</MathCourse>
</Student>
这里要注意的是XmlUtil.objectToXml(student,Student.class,MathCourse.class)这个方法的MathCourse.class是一定要传的,不然会默认用Course类转化,这样就只有Course类的属性能转成xml,子类如MathCourse中的属性会丢失。
那用上面的执行结果来转回JAVABean,测试类如下:
public String jsonToXml() throws Exception{
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
"<Student>\n" +
" <Name>张三</Name>\n" +
" <Sex>男</Sex>\n" +
" <MathCourse>\n" +
" <Id>02</Id>\n" +
" <Score>99</Score>\n" +
" <TeacherId>123</TeacherId>\n" +
" </MathCourse>\n" +
"</Student>";
Student student = (Student)XmlUtil.xmlToObject(xml,Student.class,MathCourse.class);
MathCourse mathCourse = (MathCourse)student.getCourse();
log.info("mathCourse:"+mathCourse.toString());
return xml;
}
执行结果如下:
mathCourse:MathCourse{teacherId='123', id='02', score='99'}
第二种:将JAVABean转成JSON后再转成XML
这个方法需要的依赖如下:
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>de.odysseus.staxon</groupId>
<artifactId>staxon</artifactId>
<version>1.3</version>
</dependency>
需要的工具类方法如下:
public String xml2json(String xml) {
//创建XMLSerializer对象
XMLSerializer xmlSerializer = new XMLSerializer();
//将xml转化为json
String result = xmlSerializer.read(xml).toString();
return result;
}
public String json2xml(String json) {
StringReader input = new StringReader(json);
StringWriter output = new StringWriter();
JsonXMLConfig config = new JsonXMLConfigBuilder().multiplePI(false).repairingNamespaces(false).build();
try {
XMLEventReader reader = new JsonXMLInputFactory(config).createXMLEventReader(input);
XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(output);
writer = new PrettyXMLEventWriter(writer);
writer.add(reader);
reader.close();
writer.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return output.toString();
}
测试方法如下:
public String jsonToXml() throws Exception{
Student student = new Student();
student.setName("张三");
student.setSex("男");
MathCourse mathCourse = new MathCourse();
mathCourse.setTeacherId("123");
mathCourse.setId("02");
mathCourse.setScore("99");
student.setCourse(mathCourse);
//json转xml要有一个根节点
JSONObject jsonObject = new JSONObject();
jsonObject.put("content",student);
String xml = aopUtil.json2xml(jsonObject.toJSONString());
log.info("xml:"+xml);
}
转化结果如下:
xml:<?xml version='1.0'?>
<content>
<course>
<id>02</id>
<score>99</score>
<teacherId>123</teacherId>
</course>
<name>张三</name>
<sex>男</sex>
</content>
用这个方法转成xml比较简单,需要注意的有两点,第一个就是一定要把JAVABean放到一个JSONObject里,有一个根节点,不然转化就会报错。第二个是转化的xml的节点名称就不受属性上面的@XmlElement()
注解影响了,就像上面的转化结果一样,Student类的name注解是@XmlElement(name = "Name")
,但是转化后节点名还是name。这个时候如果要改节点名的话,就要在属性上加上@JSONField()
属性,现在改一下Student类的name如下:
@XmlElement(name = "Name")
@JSONField(name = "NameJSON")
private String name;
转化的结果如下:
xml:<?xml version='1.0'?>
<content>
<NameJSON>张三</NameJSON>
<course>
<id>02</id>
<score>99</score>
<teacherId>123</teacherId>
</course>
<sex>男</sex>
</content>
这样改节点名就成功了,@JSONField()
属性用到的依赖如下
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
现在来把xml转为JSONObject,就用上面的结果来转,测试方法如下:
Student student = new Student();
student.setName("张三");
student.setSex("男");
MathCourse mathCourse = new MathCourse();
mathCourse.setTeacherId("123");
mathCourse.setId("02");
mathCourse.setScore("99");
student.setCourse(mathCourse);
//json转xml要有一个根节点
JSONObject jsonObject = new JSONObject();
jsonObject.put("content",student);
String xml = aopUtil.json2xml(jsonObject.toJSONString());
//log.info("student:"+student.toString());
//String xml = XmlUtil.objectToXml(student,Student.class,MathCourse.class);
log.info("xml:"+xml);
String json = aopUtil.xml2json(xml);
Student student1 = JSONObject.parseObject(json,Student.class);
log.info("student:"+student1.toString());
//不经过Student类直接取Course对象
Course course = JSONObject.parseObject(json).getObject("course",Course.class);
log.info("course:"+course.toString());
测试结果如下:
xml:<?xml version='1.0'?>
<content>
<NameJSON>张三</NameJSON>
<course>
<id>02</id>
<score>99</score>
<teacherId>123</teacherId>
</course>
<sex>男</sex>
</content>
student:Student{name='张三', sex='男', course=Course{id='02', score='99'}}
course:Course{id='02', score='99'}
这样转化就成功了,要注意的是JAVAObject转xml时要有一个根节点,但是转回JAVAObject的时候,是没有这个节点的,可以直接用。还有一个比较方便的地方就是可以直接绕过Student类取到Course类,在某些场景下就比较好用了,比如一个节点比较多的xml,但是你只需要其中的一部分数据,那就可以直接取,不用再创建包装的类了。