java之xml字符串转map、object转xml字符串
最近由于进行微信公众号开发,了解了一下java的一些xml操作。
所以本篇文章简单介绍xml字符串转map和object转xml字符串。
由于与微信交互的过程中一些数据微信是以xml字符串形式发送到我们服务器,然后将处理好的对象转成xml字符串发送到微信服务器。
所以需要封装个专门的工具类来处理,我建了一个类XmlUtil,用来实现上述功能。
jar包准备:
<!-- 解析xml --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency>
xml字符串转map:
xml字符串的获取:从请求request的输入流中读取Document文档对象。
用SAXReader获取List<Element>,遍历,将当前迭代元素的name和text作为键值对存入map。
具体代码如下:
/** * xml转map * * @param request * @return * @throws IOException */ public static Map<String, String> xml2Map(HttpServletRequest request) throws IOException { Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); InputStream is = null; Document doc = null; try { is = request.getInputStream(); doc = reader.read(is); Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } } catch (IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } finally { is.close(); } return map; }
接下来是XStream部分(比较重要):
private static String cdata_prefix = "<![CDATA["; private static String cdata_suffix = "]]>"; /** * 文本消息对象转Xml * * @param 对象 * @param 对象的类型 * @return */ public static String textMessage2Xml(Object object,Class<?> objectTargetClass) { XStream xStream = new XStream(new XppDriver() { /** * 对xml标签里面值含有CDATA会被转义,比如<会被转义成< * 以下代码为了能加入不被转义的CDATA */ @Override public HierarchicalStreamWriter createWriter(Writer out) { PrettyPrintWriter ppw = new PrettyPrintWriter(out) { Boolean isCdata = false; Class<?> targetClazz = null; Class<?> superClass = null; List<String> super_class_field_names = new ArrayList<String>(); List<String> target_class_field_names = new ArrayList<String>(); @Override public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) { super.startNode(name, clazz); /** * System.out.println(textMessage.getCreateTime()+",name:"+name+",clazz:"+clazz); * 输出结果 * 173413984,name:xml,clazz:class com.entity.TextMessage * 173413984,name:ToUserName,clazz:class java.lang.String * 173413984,name:FromUserName,clazz:class java.lang.String * 173413984,name:CreateTime,clazz:class java.lang.String * 173413984,name:MsgType,clazz:class java.lang.String * 173413984,name:Content,clazz:class java.lang.String * * name为xml时,clazz为xStream.alias("xml", textMessage.getClass())声明的类 */ try { if ("xml".equals(name)) { initParam(clazz); } else { if (super_class_field_names.contains(name)) { isCdata=superClass.getDeclaredField(StringUtil.firstLowerCase(name)).isAnnotationPresent(CDATA_Annotaion.class); } else if (target_class_field_names.contains(name)) { isCdata=targetClazz.getDeclaredField(StringUtil.firstLowerCase(name)).isAnnotationPresent(CDATA_Annotaion.class); } } } catch (Exception e) { e.printStackTrace(); } } private void initParam(@SuppressWarnings("rawtypes") Class clazz) { targetClazz = clazz; // 目标类属性名保存 Field[] targetFields = targetClazz.getDeclaredFields(); for (Field f : targetFields) { target_class_field_names.add(StringUtil.firstUpCase(f.getName())); } // 父类属性名保存到list中 superClass = targetClazz.getSuperclass(); if (!superClass.equals(Object.class)) { Field[] superFields = superClass.getDeclaredFields(); for (Field f : superFields) { super_class_field_names.add(StringUtil.firstUpCase(f.getName())); } } } @Override protected void writeText(QuickWriter writer, String text) { if (isCdata) { writer.write(cdata_prefix + text + cdata_suffix); } else { writer.write(text); } } }; return ppw; } }); xStream.alias("xml", objectTargetClass); //开启注解转换(别名转换) xStream.processAnnotations(objectTargetClass); return xStream.toXML(object); }
解释下上面代码:
XStream xStream = new XStream(new XppDriver() {...})
这一步中间new了个XppDriver,关于XppDriver的介绍,我查找资料后,理解为一种解析输入输出的驱动。
因为我们要在写的时候做点处理,所以重写了public HierarchicalStreamWriter createWriter(Writer out){...}方法:
PrettyPrintWriter ppw = new PrettyPrintWriter(out) {...}中重写了
@Override
public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {...}方法
和
@Override
protected void writeText(QuickWriter writer, String text) {...}方法
writeText是最后写处理,比如我要根据isCdata判断是否添加<![CDATA[]]>,isCdata为true就添加,false不添加
startNode是在节点开始的时候加入自己的代码逻辑,比如我在这边判断实体类的属性上面是否有@CDATA_Annotation,如果有我就将isCdata赋值为true,反之false。
每条实体类属性都是先startNode->writeText,比如实体类有3条属性,在startNode时候添加一条system.out.printlin语句看情况,就会发现输出3次。
/**
* System.out.println(textMessage.getCreateTime()+",name:"+name+",clazz:"+clazz);
* 输出结果
* 173413984,name:xml,clazz:class com.entity.TextMessage
* 173413984,name:ToUserName,clazz:class java.lang.String
* 173413984,name:FromUserName,clazz:class java.lang.String
* 173413984,name:CreateTime,clazz:class java.lang.String
* 173413984,name:MsgType,clazz:class java.lang.String
* 173413984,name:Content,clazz:class java.lang.String
*
* name为xml时,clazz为xStream.alias("xml", textMessage.getClass())声明的类
*/
我例子中的输出结果,类是TextMessage,里面有条属性content,剩下4条属于继承来的,做了一些处理把父类的也取到了,173413984是TextMessage类的createTime属性的值。
可以看出后面6条都是属于同一个对象,至于第一条name为xml是因为下面
生声明了类的别名为xml
解释完了上面代码,现在说下注意事项:
可能有细心的人注意到了,我用了首字母大小写转换,而且控制台输出的时候name是大写,因为我在实体类上加了XStream别名
所以startNode传进来的name会是首字母大写的形式,所以在反射的时候将其转换成了小写,为什么用别名是因为不想将实体类属性名变成首字母大写,那样不规范(普通类的类名就是大写的,会混乱),所以在不改变实体类的情况下用了别名。
如果用了别名,一定要记住在toXML方法之前一定要开启注解转换
StringUtil(首字母大写和首字母小写的方法):
public class StringUtil { public static String firstUpCase(String str) { String result=str.substring(0,1).toUpperCase()+str.substring(1,str.length()); return result; } public static String firstLowerCase(String str){ String result=str.substring(0,1).toLowerCase()+str.substring(1,str.length()); return result; } }
注解CDATA_Annotaion:
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(value = RetentionPolicy.RUNTIME) @Target(value = { ElementType.FIELD }) @Inherited public @interface CDATA_Annotaion { }
有不同的欢迎提问,有错欢迎指出!谢谢大家浏览!
ps:每天记录一点,就算写的很差,以后自己找起来也方便,也能记得更牢。
附上github:https://github.com/617355557/code_pianduan/blob/master/XmlUtil.java
欢迎大家下载,转载~~~