先上测试xml报文:
<?xml version="1.0" encoding="UTF-8"?>
<Request>
<Head>
<CODE>0000</CODE>
<id>11111</id>
<TIMESTAMP>1547016623166</TIMESTAMP>
<serviceId>serviceID</serviceId>
</Head>
<BODY>
<Orders>
<Order date="2019-01-09" price="123456" user="张三">
<Goods id="1">
<name>java从入门到放弃</name>
<price>1100</price>
<count>1</count>
<total_price>100</total_price>
</Goods>
<Goods id="1">
<name>脊椎康复指南</name>
<price>1000</price>
<count>1</count>
<total_price>1000</total_price>
</Goods>
</Order>
<Order date="2019-01-09" price="11000" user="阿珍爱上了阿强">
<Goods id="1">
<name>java从入门到放弃</name>
<price>1100</price>
<count>1</count>
<total_price>100</total_price>
</Goods>
</Order>
</Orders>
<userId>1111</userId>
<userName>王二麻子</userName>
<Date>2019-01-09 14:50:23</Date>
</BODY>
</Request>
一、核心XmlAttribleSub类
public class XmlAttribleSub implements Serializable {
private static final long serialVersionUID = -4651442473666480694L;
/** 节点所在层级 */
private int id;
/** 节点名称 */
private String name;
/** 节点值 基本节点才有值 */
private String value = "";
/** 属性map */
private Map<String, String> attrs = new HashMap<>();
/** 子节点列表 */
private List<XmlAttribleSub> subNodes = new ArrayList<>();
public XmlAttribleSub() {}
public XmlAttribleSub(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getAttrs() {
return attrs;
}
public void addAttr(String name, String value) {
this.attrs.put(name, value);
}
public List<XmlAttribleSub> getSubNodes() {
return subNodes;
}
public String getValue() {
return value == null ? "" : value;
}
public void setValue(String value) {
this.value = value;
}
public void addSubNode(XmlAttribleSub subNode) {
this.subNodes.add(subNode);
}
public boolean isRoot() {
return this.id == 0;
}
public boolean isHasAttr() {
return this.attrs.size() > 0;
}
public boolean isHasSubNode() {
return this.subNodes.size() > 0;
}
public boolean isUnderNode() {
return !isHasAttr() && !isHasSubNode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.id == 0) {
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
sb.append("<").append(this.name);
if (CollectionUtil.isNotBlank(attrs)) {
for (Map.Entry<String, String> entry : attrs.entrySet()) {
sb.append(" ").append(entry.getKey()).append("=").append("\"").append(entry.getValue())
.append("\"");
}
}
sb.append(">");
if (!subNodes.isEmpty()) {
for (XmlAttribleSub subNode : subNodes) {
recSub(subNode, sb);
}
} else {
sb.append(this.getValue());
}
sb.append("</").append(this.name).append(">");
return sb.toString();
}
private void recSub(XmlAttribleSub sub, StringBuilder sb) {
sb.append("<").append(sub.getName());
if (CollectionUtil.isNotBlank(sub.getAttrs())) {
for (Map.Entry<String, String> entry : sub.getAttrs().entrySet()) {
sb.append(" ").append(entry.getKey()).append("=").append("\"").append(entry.getValue())
.append("\"");
}
}
sb.append(">");
if (sub.getSubNodes().isEmpty()) {
sb.append(sub.getValue());
} else {
for (XmlAttribleSub subNode : sub.getSubNodes()) {
recSub(subNode, sb);
}
}
sb.append("</").append(sub.getName()).append(">");
}
}
XmlAttribleSub类是抽象出来的公共xml封装类,含有以下五个属性:
- id:当前节点所在层级,根节点为0,往里递增。测试报文Request为根节点,id=0;Head和Body的id为1;
- name:当前节点名称;
- value:节点值,只有基本节点才有值,即attrs和subNodes都为空,比如测试报文name、price节点等;
- attrs:节点属性map,当前节点的属性的键值对集合,比如测试报文Order节点的date,user属性等;
- subNodes:当前节点的子节点列表,list元素也是XmlAttribleSub;
二、工具类Dom4Util
Dom4Util类提供xml转换方法,包括xml转换成XmlAttribleSub,xml转换成bean,bean转换xml等。
/**
* 从根节点开始,将xml转换成XmlAttribleSub
*
* @param xml
* @return
* @throws Exception
*/
public static XmlAttribleSub xPathAttribleSub(String xml) throws Exception {
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
XmlAttribleSub sub = new XmlAttribleSub(0, root.getName());
recParse(root, sub, 0);
return sub;
}
/**
* 递归解析xml
*
* @param root
* @param sub
* @param level
*/
private static void recParse(Element root, XmlAttribleSub sub, int level) {
level++;
if (root.isTextOnly()) {
// 该节点没有字节点
sub.setValue(root.getText());
}
// 设置当前节点属性map
for (Iterator it = root.attributeIterator(); it.hasNext();) {
Attribute attr = (Attribute) it.next();
sub.getAttrs().put(attr.getName(), attr.getValue());
}
// 递归子节点
for (Iterator iter = root.elementIterator(); iter.hasNext();) {
Element element = (Element) iter.next();
XmlAttribleSub subNode = new XmlAttribleSub(level, element.getName());
for (Iterator it = element.attributeIterator(); it.hasNext();) {
Attribute attr = (Attribute) it.next();
subNode.getAttrs().put(attr.getName(), attr.getValue());
}
if (element.isTextOnly()) {
subNode.setValue(element.getText());
} else {
recParse(element, subNode, level);
}
sub.addSubNode(subNode);
}
}
从根节点开始,解析整个xml为XmlAttribleSub,这里借助了Document来解析xml报文。
/**
* 根据指定节点开始,将xml转换成XmlAttribleSub列表
*
* @param xml
* @param xPathId 报文节点,例如//Orders/Order/goods/name
* @return
*/
public static List<XmlAttribleSub> xPathAttribleSub(String xml, String xPathId) throws Exception {
List<XmlAttribleSub> subs = new ArrayList<>();
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
// 根据指定节点获取节点
List<Element> elements = root.selectNodes(xPathId);
// 判断当前节点的层级,从根节点0开始
int level = xPathId.substring(1).split("/").length;
for (Element element : elements) {
XmlAttribleSub sub = new XmlAttribleSub(level - 2, element.getName());
recParse(element, sub, level - 2);
subs.add(sub);
}
return subs;
}
从制定节点开始解析,例如解析报文中的Goods节点,xPathId为//Request/BODY/Orders/Order/goods,返回的是一个list。
三、XmlAttribleSub转换成bean
这里需要添加两个注解Xml和Level,来标注bean对应的节点名称、层级等。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
public @interface Xml {
/**
* 注解是否是节点属性,默认为false,没有默认值添加注解时必填
*
* @return
*/
boolean isAttr() default false;
/**
* 标志bean属性是否是xml元素,默认为false,是xml元素的属性需要设置为true
*
* @return
*/
boolean isElement() default false;
/**
* 属性是否是list,默认为false
*
* @return
*/
boolean isList() default false;
/**
* 属性bean的类型或list中bean的类型
*
* @return
*/
Class clazz() default Object.class;
/**
* 属性对应的xml节点名称,一样不需要设置
*
* @return
*/
String xmlName() default "";
/**
* 配置在Level注解中时必须配置bean的属性
*
* @return
*/
String attrName() default "";
/**
* xml转date类型的格式
*
* @return
*/
String dateFormat() default "yyyy-MM-dd HH:mm:ss";
int id() default -1;
}
Xml注解属性:
- isAttr:标注bean的属性是否是对应xml节点的属性,默认为false;
- isElement:标注bean的属性是否需要映射成xml的节点或节点属性,默认为false,这个对于bean转换成xml需要;
- isList:标注bean的属性是否对应xml的list节点(该类对应节点包含一个list节点),例如Body节点的Orders节点,默认为false,如果isList配置为true,则必须配置clazz;
- clazz:bean的属性对应节点的对象的class,默认为Object.class;
- xmlName:bean的属性对应xml节点或节点属性的名称,默认为空,如果bean属性名和xml节点名一致,可以不配置;
- attrName:配置bean的属性名称,默认为空,当xml注解配置在Level注解中时需要配置;
- dateFormat:bean的属性对应Date类型时,需要配置时间格式化字符串,默认为yyyy-MM-dd HH:mm:ss;
Xml注解可以添加在属性上,也可以添加在Level注解中。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface Level {
/**
* 类对应节点所在的层级
*
* @return
*/
int value() default -1;
/**
* 类对应的节点名,不设置默认为类名
*
* @return
*/
String xmlName() default "";
Xml[] xmls() default {@Xml()};
/**
* 标志当前节点映射的bean就是一个list
*
* @return
*/
boolean isList() default false;
}
Level节点的属性:
- value:节点的层级,默认为-1,根节点为0,往里递增;
- xmlName:类对应的接待你名称,如果类名与对应节点名称不一致,需要配置正确的节点名称,默认为空;
- xmls:类的属性xml配置集合,可以在这里集中配合类的属性Xml注解,默认为一个空的Xml注解数组;
- isList:当前类是否对应xml的list节点,例如测试报文中的Order节点就是一个list节点,默认为false;
Level节点只能配置在类上。
下面是测试报文的映射Bean:
1、根节点映射Bean,也是整个报文的映射bean,Request:
@Getter
@Setter
@ToString
@Level(value = 0, xmls = {@Xml(attrName = "head", xmlName = "HEAD", clazz = Head.class, isElement = true),
@Xml(attrName = "body", xmlName = "BODY", clazz = Body.class, isElement = true)})
public class Request {
private Head head;
private Body body;
}
根节点Reque包含两个对象Head和Body,这里Head属性不会变化,Body类抽出固定的属性,在其子类扩展额外属性,像这里的Orders节点。这里body节点的xml注解clazz配置的是父类Body.class,通过以下方法动态改变注解的clazz属性值:
/**
* 根据body的实体类获取不同Request
*
* @param reClazz root节点的映射bean的class
* @param bodyClazz body节点中额外的节点映射bean的class
* @param <T>
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
public static <T> Class<T> getXmlClass(Class<T> reClazz, Class bodyClazz)
throws NoSuchFieldException, IllegalAccessException {
Level level = reClazz.getAnnotation(Level.class);
Xml[] xmls = level.xmls();
for (Xml xml : xmls) {
if (xml.attrName().equals("body")) {
ReflectUtil.changeAnnotationValue(xml, "clazz", bodyClazz);
}
}
return reClazz;
}
修改注解属性值的方法:
/**
* 修改注解的属性值
*
* @param annotation
* @param name
* @param value
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
public static void changeAnnotationValue(Annotation annotation, String name, Object value)
throws NoSuchFieldException, IllegalAccessException {
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
Field f = handler.getClass().getDeclaredField("memberValues");
f.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) f.get(handler);
memberValues.put(name, value);
}
2、//Request/Head节点的映射类Head:
@Getter
@Setter
@ToString
@Level(value = 1, xmls = {@Xml(attrName = "code", xmlName = "CODE", isElement = true),
@Xml(attrName = "timestamp", xmlName = "TIMESTAMP", isElement = true)})
public class Head {
private String code;
@Xml(isElement = true)
private String id;
private Timestamp timestamp;
@Xml(isElement = true)
private String serviceId;
}
这里,Xml注解可以注解在属性上,也可以集中注解在Level注解中,如果属性重复注解,以属性上的Xml注解为准。
3、Body类:
@Getter
@Setter
@ToString
@Level(value = 1, xmlName = "BODY", xmls = {
@Xml(attrName = "userId", isElement = true),
@Xml(attrName = "userName", isElement = true)
})
public class Body {
private long userId;
private String userName;
@Xml(xmlName = "Date", isElement = true)
private Date date;
}
父类Body包含该节点的固定属性节点以及节点Xml注解配置和当前节点的Level配置。
//Request/BODY节点的映射类,Body的子类OrderBody:
public class OrderBody extends Body {
@Xml(xmlName = "Orders", isList = true, clazz = Order.class, isElement = true)
private List<Order> orders;
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
@Override public String toString() {
String s = super.toString();
return s.substring(0, s.length() - 1) + ", orders=" + orders + ")";
}
}
Body的子类可以继承其父类的属性及Xml注解配置和Level注解配置。
4、//Request/BODY/Orders/Order节点的映射类Order:
@Level(value = 1, isList = true,
xmls = {@Xml(attrName = "user", isAttr = true, isElement = true),
@Xml(attrName = "date", isAttr = true, isElement = true, dateFormat = "yyyy-MM-dd"),
@Xml(attrName = "price", isAttr = true, isElement = true), @Xml(attrName = "goodsList",
xmlName = "Order", clazz = Goods.class, isElement = true, isList = true)})
public class Order {
// @Xml(isAttr = true, isElement = true)
private String user;
// @Xml(isAttr = true, isElement = true)
private Date date;
// @Xml(isAttr = true, isElement = true)
private String price;
// @Xml(clazz = Goods.class, isElement = true, isList = true)
private List<Goods> goodsList = new ArrayList<>();
public void add(Goods goods) {
this.goodsList.add(goods);
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public List<Goods> getGoodsList() {
return goodsList;
}
public void setGoodsList(List<Goods> goodsList) {
this.goodsList = goodsList;
}
@Override
public String toString() {
return "Order{" + "user='" + user + '\'' + ", date='" + date + '\'' + ", price='" + price + '\''
+ ", goodsList=" + goodsList.toString() + '}';
}
}
5、//Request/BODY/Orders/Order/Goods节点的映射类Goods:
@Getter
@Setter
@ToString
@Level(0)
public class Goods {
@Xml(id = 0, isAttr = true, isElement = true)
private String id;
@Xml(id = 1, xmlName = "name", isElement = true)
private String name;
@Xml(isElement = true)
private String price;
@Xml(isElement = true)
private int count;
@Xml(isElement = true, xmlName = "total_price")
private String totalPrice;
}
bean配置文成,上菜刀了,Dom4Util类的xml解析成对应bean的方法:
/**
* XmlAttribleSub转换成bean
*
* @param sub
* @param clazz
* @param <T>
* @return
* @throws Exception
*/
public static <T> T xmlSubToBean(XmlAttribleSub sub, Class<T> clazz) throws Exception {
if (!sub.isHasAttr() && !sub.isHasSubNode()) {
throw new Exception(sub + "既没有属性也没有子节点");
}
T t = clazz.newInstance();
Xml[] xmls = XmlAnnotationUtil.getClassXmls(clazz);
Field field;
// 解析属性
for (Map.Entry<String, String> entry : sub.getAttrs().entrySet()) {
field = XmlAnnotationUtil.getBeanField(clazz, xmls, entry.getKey());
Xml xml = XmlAnnotationUtil.getXml(xmls, entry.getKey());
if (xml != null) {
if (DataType.isDate(field.getType().getTypeName())) {
ReflectUtil.setField(t, field, DateUtil.parseDate(entry.getValue(), xml.dateFormat()));
} else {
ReflectUtil.setField(t, field, entry.getValue());
}
}
}
// 解析子节点
Xml xml;
Object o;
Level level = clazz.getAnnotation(Level.class);
if (level != null && level.isList()) {
// 如果当前节点就是一个list
List list = new ArrayList();
xml = XmlAnnotationUtil.getListXml(xmls);
if (xml != null) {
field = XmlAnnotationUtil.getBeanField(clazz, xmls, sub.getName());
for (XmlAttribleSub xmlAttribleSub : sub.getSubNodes()) {
list.add(xmlSubToBean(xmlAttribleSub, xml.clazz()));
}
ReflectUtil.setField(t, field, list);
}
} else {
// 遍历当前节点的子节点
for (XmlAttribleSub xmlAttribleSub : sub.getSubNodes()) {
xml = XmlAnnotationUtil.getXml(xmls, xmlAttribleSub.getName());
if (xml != null) {
field = XmlAnnotationUtil.getBeanField(clazz, xmls, xmlAttribleSub.getName());
if (xml.isList()) {
// 当前sub的子节点是一个list的元素
Class subClazz = XmlAnnotationUtil.getSubNodeBeanClazz(xmls, xmlAttribleSub.getName());
if (subClazz == null) {
throw new Exception(xmlAttribleSub.getName() + "节点没有配置映射clazz");
}
List list = new ArrayList();
for (XmlAttribleSub s : xmlAttribleSub.getSubNodes()) {
// 子节点是否还有子节点或有属性
if (s.isHasSubNode() || s.isHasAttr()) {
list.add(xmlSubToBean(s, subClazz));
} else {
log.warn("list存在空节点");
}
}
ReflectUtil.setField(t, field, list);
} else if (!Object.class.getTypeName().equals(xml.clazz().getTypeName())) {
// 子节点是bean
o = xmlSubToBean(xmlAttribleSub, xml.clazz());
ReflectUtil.setField(t, field, o);
} else if (xmlAttribleSub.isUnderNode()) {
// 当前节点是普通节点
if (DataType.DATATYPE_DATE.equals(field.getType().getTypeName())) {
ReflectUtil.setField(t, field,
DateUtil.parseDate(xmlAttribleSub.getValue(), xml.dateFormat()));
} else {
ReflectUtil.setField(t, field,
DataType.transfer(xmlAttribleSub.getValue(), field.getType()));
}
}
}
}
}
return t;
}
这里,反射对象的属性用到了工具类ReflectUtil,获取对象的所有属性,包括父类的属性:
/**
* 获取类的所有属性,包括父类的所有属性
*
* @param clazz
* @return
*/
public static Field[] getAllFields(Class clazz) {
List<Field> fieldList = new ArrayList<>();
recurField(fieldList, clazz);
return CollectionUtil.toArray(fieldList);
}
private static void recurField(List<Field> fieldList, Class clazz) {
if (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
fieldList.addAll(Arrays.asList(fields));
recurField(fieldList, clazz.getSuperclass());
}
}
还有注解工具类XmlAnnotationUtil的方法:
/**
* 获取xml映射bean的所有xml注解
*
* @param clazz
* @return
*/
public static Xml[] getClassXmls(Class clazz) throws Exception {
List<Xml> xmlList = new ArrayList<>();
Level level = (Level) ReflectUtil.getAnnotation(clazz, Level.class);
Xml[] xs = null;
if (levelHasXml(level)) {
xs = level.xmls();
}
Field[] fields = ReflectUtil.getAllFields(clazz);
if (CollectionUtil.isNotBlank(fields)) {
Field f;
for (Field field : fields) {
Xml xml = field.getAnnotation(Xml.class);
if (xml != null) {
// 设置xml的attrName和xmlName
InvocationHandler handler = Proxy.getInvocationHandler(xml);
f = handler.getClass().getDeclaredField("memberValues");
f.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) f.get(handler);
memberValues.put("attrName", field.getName());
if (xml.xmlName().isEmpty()) {
memberValues.put("xmlName", field.getName());
}
// 以属性上的注解为主,level注解中配置的xml会被覆盖掉
xmlList.add(xml);
if (xs != null) {
for (int i = 0; i < xs.length; i++) {
if (field.getName().equals(xs[i].attrName())) {
xs[i] = null;
}
}
}
}
}
}
if (CollectionUtil.isNotBlank(xs)) {
for (Xml xml : xs) {
if (xml != null) {
xmlList.add(xml);
}
}
}
return CollectionUtil.toArray(xmlList);
}
还有bean转换成XmlAttribleSub方法:
public static XmlAttribleSub beanToXmlSub(Object obj) throws Exception {
Class clazz = obj.getClass();
Level level = (Level) ReflectUtil.getAnnotation(clazz, Level.class);
int nodeLevel = 0;
Xml[] xmls = XmlAnnotationUtil.getClassXmls(clazz);
String classXmlName = clazz.getSimpleName();
if (level != null) {
nodeLevel = level.value();
classXmlName = level.xmlName().length() > 0 ? level.xmlName() : classXmlName;
} else {
log.warn("转xml的类{}未添加注解标志节点级别,默认为root", clazz.getName());
}
XmlAttribleSub root = new XmlAttribleSub(nodeLevel, classXmlName);
Field[] fields = ReflectUtil.getAllFields(clazz);
XmlAttribleSub sub;
Xml xml;
String attrName;
Object attrValue;
for (Field field : fields) {
xml = XmlAnnotationUtil.getXml(xmls, field.getName());
field.setAccessible(true);
try {
attrValue = field.get(obj);
if (xml != null) {
if (xml.isElement()) {
// 节点属性是xml元素
if (!Object.class.getName().equals(xml.clazz().getName())) {
if (xml.isList()) {
List list = (List) attrValue;
// XmlAttribleSub xmlAttribleSub = root;
if (level.isList()) {
// 当前节点是list
if (CommonUtils.isNotEmpty(list)) {
for (Object subNode : list) {
root.addSubNode(beanToXmlSub(subNode));
}
}
} else {
// 当前节点包含list节点
XmlAttribleSub xmlAttribleSub = new XmlAttribleSub(nodeLevel + 1, xml.xmlName());
if (CommonUtils.isNotEmpty(list)) {
for (Object subNode : list) {
xmlAttribleSub.addSubNode(beanToXmlSub(subNode));
}
}
root.addSubNode(xmlAttribleSub);
}
} else {
// 当前节点有bean子节点
root.addSubNode(beanToXmlSub(attrValue));
}
} else if (xml.isAttr()) {
// bean属性添加了xml注解,并且标志为xml节点属性
attrName = "".equals(xml.xmlName()) ? field.getName() : xml.xmlName();
String value = DataType.isDate(attrValue)
? DateUtil.formatDate((Date) attrValue, xml.dateFormat())
: DataType.getAsStringNotNull(attrValue);
// 设置属性
root.addAttr(attrName, value);
} else {
// 属性是普通节点
attrName = "".equals(xml.xmlName()) ? field.getName() : xml.xmlName();
sub = new XmlAttribleSub(nodeLevel + 1, attrName);
String value = DataType.isDateType(attrValue)
? DateUtil.formatDate((Date) attrValue, xml.dateFormat())
: DataType.getAsStringNotNull(attrValue);
sub.setValue(DataType.getAsStringNotNull(value));
root.addSubNode(sub);
}
}
}
} catch (IllegalAccessException e) {
root = null;
log.error("bean转换成XmlAttribleSub异常", e);
}
}
return root;
}
根节点的XmlAttribleSub对象的tostring方法可以转换成xml报文:
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.id == 0) {
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
sb.append("<").append(this.name);
if (CollectionUtil.isNotBlank(attrs)) {
for (Map.Entry<String, String> entry : attrs.entrySet()) {
sb.append(" ").append(entry.getKey()).append("=").append("\"").append(entry.getValue())
.append("\"");
}
}
sb.append(">");
if (!subNodes.isEmpty()) {
for (XmlAttribleSub subNode : subNodes) {
recSub(subNode, sb);
}
} else {
sb.append(this.getValue());
}
sb.append("</").append(this.name).append(">");
return sb.toString();
}
private void recSub(XmlAttribleSub sub, StringBuilder sb) {
sb.append("<").append(sub.getName());
if (CollectionUtil.isNotBlank(sub.getAttrs())) {
for (Map.Entry<String, String> entry : sub.getAttrs().entrySet()) {
sb.append(" ").append(entry.getKey()).append("=").append("\"").append(entry.getValue())
.append("\"");
}
}
sb.append(">");
if (sub.getSubNodes().isEmpty()) {
sb.append(sub.getValue());
} else {
for (XmlAttribleSub subNode : sub.getSubNodes()) {
recSub(subNode, sb);
}
}
sb.append("</").append(sub.getName()).append(">");
}
这里面用到了一些自己写的工具类,详细代码请移步码云。
转换测试xml报文为Request对象
Request request = Dom4Util.xmlToBean(xml, XmlAnnotationUtil.getXmlClass(Request.class, OrderBody.class));