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

欢迎大家下载,转载~~~

posted on 2018-05-01 00:26  康纳酱  阅读(6740)  评论(0编辑  收藏  举报

导航