链接地址:http://www.xx566.com/detail/155.html
在日常的工作中,我们经常需要处理xml格式的字符串,比如:调用第三方接口返回xml信息,需要解析xml获取相应的结果信息,之前自己写过一个利用 Java反射解析XML字符串,并封装到指定的JavaBean中的方法,最近的工作中又使用到了第三方接口,需要处理接口返回的XML字符串信息,对解 析XML的工具方法做了重新的优化,这里记录总结一下。
首先, 我们看下面xml格式的字符串:
//这里随便在网上搜索的xml字符串,简单做了修改 firstXmlStr = "<?xml version=\"1.0\" encoding=\"GBK\"?>" + "<Result xmlns=\"http://www.xx566.com\">" + "<row resultcount=\"1\">" + "<users_id>1001 </users_id>" + "<users_name>Realfighter </users_name>" + "<users_group>81 </users_group>" + "<users_address>1001号 </users_address>" + "</row>" + "</Result>";
假设现在我们需要获取到其中的结果信息,拿到用户的相关信息,并封装到指定的UserBean中,如下:
/** * xml中需要的信息封装到User * 这里继承BaseObject用于第二个解析xml的方法 * 注意:User的属性名必须与xml字符串中的标签一致 */ class User extends BaseObject { String users_id;//用户ID String users_name;//用户名 String users_group;//用户分组 String users_address;//用户地址 public String getUsers_id() { return users_id; } public void setUsers_id(String users_id) { this.users_id = users_id; } public String getUsers_name() { return users_name; } public void setUsers_name(String users_name) { this.users_name = users_name; } public String getUsers_group() { return users_group; } public void setUsers_group(String users_group) { this.users_group = users_group; } public String getUsers_address() { return users_address; } public void setUsers_address(String users_address) { this.users_address = users_address; } @Override public String toString() { return "User{" + "users_id=" + users_id + ", users_name='" + users_name + '\'' + ", users_group=" + users_group + ", users_address='" + users_address + '\'' + '}'; } }
我们通过以下方法解析xml并封装到指定对象,如下:
/** * 封装xml信息到指定的Object * * @param returnXml * @param obj * @return Object * @throws * @Title: packReturnByXml * @author Realfighter */ public static Object packReturnByXml(String returnXml, Object obj) { Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); String fieldname = field.getName(); try { Pattern p = Pattern.compile("<" + fieldname + ">.*?</" + fieldname + ">"); //匹配类似:<users_id>1001 </users_id>的字符 Matcher matcher = p.matcher(returnXml); while (matcher.find()) { Pattern p1 = Pattern.compile(">([^<>]*)<"); //匹配类似:>1001 <的字符 Matcher m1 = p1.matcher(matcher.group(0).trim()); if (m1.find()) { String value = m1.group(1).trim().equals("") ? null : m1.group(1).trim(); Class<?> beanType = field.getType(); String setMethodName = "set" + fieldname.substring(0, 1).toUpperCase() + fieldname.substring(1); Method m = obj.getClass().getMethod(setMethodName, beanType); m.invoke(obj, value); } } } catch (Exception e) { continue; } } return obj; }
测试如下:
@Test public void testFirstXmlStr() { User user = (User) packReturnByXml(firstXmlStr, new User()); System.out.println(user.toString()); //执行结果: //User{users_id=1001, users_name='Realfighter', users_group=81, users_address='1001号'} }
但是如果我们获取到的xml字符串是以下的类型,上面的方法就存在问题了,因为我们匹配到的不止是一条记录,我们需要的也是多个对象,如下:
secondXmlStr = "<?xml version=\"1.0\" encoding=\"GBK\"?>" + "<Result xmlns=\"http://www.xx566.com\">" + "<row resultcount=\"1\">" + "<users_id>1001 </users_id>" + "<users_name>Realfighter </users_name>" + "<users_group>81 </users_group>" + "<users_address>1001号 </users_address>" + "</row>" + "<row resultcount=\"2\">" + "<users_id>1002 </users_id>" + "<users_name>Realfighter2 </users_name>" + "<users_group>82 </users_group>" + "<users_address>1002号 </users_address>" + "</row>" + "</Result>";
这时候,我们就需要改进一下上面的packReturnByXml方法,对其中匹配到的对象放入数组返回,如下:
/** * 解析xml,封装成returnbean数组 * * @param returnXml * @param obj * @return * @throws IllegalArgumentException * @throws IllegalAccessException * @throws SecurityException * @throws NoSuchMethodException * @throws InvocationTargetException */ public static BaseObject[] packReturnArrayByXml(String returnXml, BaseObject obj) throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchMethodException, InvocationTargetException { Field[] fields = obj.getClass().getDeclaredFields(); List<Object> objs = new ArrayList<Object>(); int count = 0;//临时变量,用于计算可匹配到的对象数目 for (Field field : fields) { field.setAccessible(true); String fieldname = field.getName(); Pattern p = Pattern.compile("<" + fieldname + ">.*?</" + fieldname + ">"); Matcher matcher = p.matcher(returnXml); while (matcher.find()) { //对象的复制方法 Object _obj = obj.clone(); objs.add(_obj); count++; } if (count > 0) { //一旦获取到对象数组,结束循环 break; } } BaseObject[] objs2 = new BaseObject[count]; for (int i = 0; i < count; i++) { Object obj1 = objs.get(i); for (Field field : fields) { field.setAccessible(true); String fieldname = field.getName(); try { Pattern p = Pattern.compile("<" + fieldname + ">.*?</" + fieldname + ">"); Matcher matcher = p.matcher(returnXml); int num = 0; while (matcher.find()) { if (num == i) { Pattern p1 = Pattern.compile(">([^<>]*)<"); Matcher m1 = p1.matcher(matcher.group(0).trim()); if (m1.find()) { String value = m1.group(1).trim().equals("") ? null : m1.group(1).trim(); Class<?> beanType = field.getType(); String setMethodName = "set" + fieldname.substring(0, 1) .toUpperCase() + fieldname.substring(1); Method m = obj1.getClass().getMethod( setMethodName, beanType); m.invoke(obj1, value); } } num++; } } catch (NullPointerException e) { continue; } } objs2[i] = (BaseObject) obj1; } return objs2; }
这里需要传入的是一个BaseObject对象,实现Cloneable接口的一个基类,实际的User对象需要继承此对象,以实现clone(),如下:
/** * 基础的Object对象,实现Cloneable接口,用于调用clone() */ class BaseObject implements Cloneable { @Override public Object clone() { Object obj = null; try { obj = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return obj; } }
测试如下:
@Test public void testSecondXmlStr() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { BaseObject[] objs = packReturnArrayByXml(secondXmlStr, new User()); for (BaseObject base : objs) { System.out.println(((User) base).toString()); } //执行结果: //User{users_id=1001, users_name='Realfighter', users_group=81, users_address='1001号'} //User{users_id=1002, users_name='Realfighter2', users_group=82, users_address='1002号'} }
总结:在XML信息比较简单的时候,不需要封装多个对象信息的时候,我们可以调用第一个方法:packReturnByXml解析xml封装到指定的对象 中,但是当xml信息比较复杂的时候,需要封装多个对象信息的时候,我们就需要将封装的对象继承BaseObject,通过调用 packReturnArrayByXml返回BaseObject数组,通过遍历数组拿到需要的相关信息。
源码地址:http://git.oschina.net/realfighter/xx566-diary/blob/master/src/javase/reflect/XmlUtils.java