近期由于公司业务原因需要调用其他公司的接口,然后格式确实XML字符串的(ke so, 使用json他不香么…).于是自己整理了一个XML和JAVA对象互转的工具,上代码…
先交代下需要使用到的依赖,这边使用了dom4j的maven jar包。
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
先来一个XML字符串转JAVA对象的方法:
public static <T> T parseObject(String content, Class<T> clazz) throws Exception {
Document document = DocumentHelper.parseText(content);
Element rootElement = document.getRootElement();
return generateObject(rootElement, clazz);
}
private static<T> T generateObject(Element element, Class<T> clazz) throws Exception {
T instance = clazz.newInstance();
List<Element> elements = element.elements();
elements.forEach(e -> doParse(e, instance));
return instance;
}
主要是doPase方法,递归解析XML格式字符:
private static <T> void doParse(Element element, T instance) {
String name = element.getName();
String text = element.getText();
Class<?> clazz = instance.getClass();
try{
// 主要是通过反射获取,设置字段属性,
Field field = clazz.getDeclaredField(name);
setAttribute(field, instance, text, element);
List<Element> elements = element.elements();
if (!elements.isEmpty()) {
for (Element ele : elements) {
doParse(ele, field);
}
}
}catch(Exception e){
// 异常自己处理,偷个懒,嘿嘿...另外为了lambda表达式的美观起见,异常不往外抛了,颜值很重要~
e.printStackTrace();
}
}
private static<T> void setAttribute(Field field, T obj, String value, Element element) {
field.setAccessible(true);
Class<?> fieldType = field.getType();
try{
if (fieldType.isAssignableFrom(String.class)) {
field.set(obj, value);
} else if (fieldType.isAssignableFrom(Integer.class)) {
field.set(obj, Integer.parseInt(value));
// 这里需要添加一些基本类型的转换(手法比较粗糙)
} else {
// 如果是自定义的对象,递归。。。
Object instance = generateObject(element, fieldType);
field.set(obj, instance);
}
}catch(Exception e){
e.printStackTrace();
}
}
测试走一把~,交代下测试的JAVA对象,Person对象里面有个City的对象(里面的注解后面会讲到,自己定义,主要是为了JAVA转XML)
@XmlHead(name = "User")
public class Person {
@XmlProperty(name = "name")
private String name;
@XmlProperty(name = "address")
private String address;
@XmlProperty(name = "age")
private Integer age;
@XmlProperty(name = "city")
private City city;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void setCity(City city) {
this.city = city;
}
}
@XmlHead(name = "city")
public class City {
@XmlProperty(name = "province")
private String province;
@XmlProperty(name = "city")
private String city;
@XmlProperty(name = "area")
private String area;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
}
public static void main(String[] args) throws Exception {
String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<User>\n" +
"\t<name>chris</name>\n" +
"\t<address>shanghai</address>\n" +
"\t<age>33</age>\n" +
"\t<city>\n" +
"\t\t<province>zhejiang</province>\n" +
"\t\t<city>jiaxing</city>\n" +
"\t\t<area>海盐</area>\n" +
"\t</city>\n" +
"</User>";
Person person = parseObject(str, Person.class);
}
看下效果,可以看到XML格式的字符串成功转成JAVA对象:
接着来看下JAVA对象转XML的方法:
public static <T> String getXmlString(T obj) throws Exception {
Document doc = DocumentHelper.createDocument();
Class<?> clazz = obj.getClass();
XmlHead annotation = clazz.getAnnotation(XmlHead.class);
String name = annotation.name();
Element rootEle = doc.addElement(name);
addSubEle(rootEle, obj);
String ret = getXmlStringFromDocument(doc, Encoding.UTF_8);
return ret;
}
enum Encoding {
UTF_8("UTF-8"),
GBK("GBK");
private String value;
Encoding(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
主要的逻辑在addSubEle这个方法里面,往下看:
Class<?> aClass = instance.getClass();
XmlHead xmlHead = aClass.getDeclaredAnnotation(XmlHead.class);
// 这个注解是打在类上的,表明这个类下面有子节点
if (null == xmlHead) {
return;
}
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
XmlProperty xmlProperty = field.getDeclaredAnnotation(XmlProperty.class);
// 这个注解是打字段上的,用于判断是否解析子节点
if (null == xmlProperty) {
continue;
}
Class<?> fieldType = field.getType();
XmlHead head = fieldType.getDeclaredAnnotation(XmlHead.class);
if (head != null) {
// 当前节点下有子节点,向下递归
Element element = rootEle.addElement(head.name());
field.setAccessible(true);
addSubEle(element, field.get(instance));
} else {
Element element = rootEle.addElement(xmlProperty.name());
field.setAccessible(true);
Object fValue = field.get(instance);
if (null != fValue) {
element.setText(fValue.toString());
}
}
最后把document输出为字符串:
public static String getXmlStringFromDocument(Document document, Encoding encoding) {
StringWriter sw = new StringWriter();
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding(encoding.getValue());
try {
XMLWriter xmlWriter = new XMLWriter(sw, format);
xmlWriter.write(document);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
sw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return sw.toString();
}
自定义注解如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface XmlHead {
String name();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XmlProperty {
String name();
}
最后再测试一下~
Person person = new Person();
person.setName("chris");
person.setAge(33);
person.setAddress("shanghai");
City city = new City();
city.setProvince("zhejiang");
city.setCity("jiaxing");
city.setArea("haiyan");
person.setCity(city);
String xmlString = getXmlString(person);
System.out.println(xmlString);
结果如下:
小结
主要使用了反射加递归的思路写了一个小工具完成XML格式字符串和JAVA对象的相互转换,对于JAVA对象字段的属性类型判断比较粗糙,有待优化(属性名字需要和XML节点的值相同)。