目前Java对象与XML文互转已经有一些现成的解决方案,例如XStream。但是我对他的性能产生了一定的怀疑(XStream依赖一些其他的组件),当然还有一个原因是由于项目本身的原因,不希望Web层和接口层互相之间有过多的依赖(使用XStream在接口层将XML转化为对象之后需要将对象传到Web层)。项目是一个分布式系统,前端是web层,后端是一个接口系统,主要用于web系统与其他系统的接口,其中一种接口使用的纯粹的XML文来作为数据交换的,这个接口系统需要将web层传过来的请求(XML文件)传递到外部系统,外部系统将处理的结果提以XML文件返回过来。业务层面已经定了好了所有的XML的请求与响应片段,主要是找到一种合理的方法来实现XML到Java对象的互转,方便前段Web系统的开发。整个处理中只使用了JDom解析XML,未使用其他的工具包。 我的设计如下: 1.在Web层需要调用外部系统接口的数据使用一些特殊的POJO来封装,他们都实现一个统一的接口,通过对该接口的调用,可以将POJO对象的数据转化为Map+List的封装方式,然后将这个Map+List传递到接口层,注意:这里与传递自定义的对象有一定的区别,Map+List内部封装的都是String类型的数据,不会在Web层和接口层之间导致依赖; 2.在接口层通过将Map+List转化为XML文,然后通过接口传递给调用系统; 3.外部系统将处理结果以XML文件返回,然后接口系统将XML转化为Map+List的对象,然后将此类对象传递到Web层,Web层直接根据业务协议从Map中获取对应的值,实际上这个传递过程不是对称的。 以下为具体的代码,供大家参考: Web层的代码: Web层所有的请求对象都要实现该接口 public interface MarshalEnable { /** * 把对象内容按key=节点名,value=节点值的方式组装成hashmap * @return HashMap */ @SuppressWarnings("unchecked") public Map marshal(); } 所有请求对象的基类: public abstract class BaseReq implements MarshalEnable { //协议头常量定义 private static final String MSG_ID = "MSG_ID"; private static final String SERVICE_CD = "SERVICE_CD"; private static final String FROM = "FROM"; private static final String TO = "TO"; private static final String TIME = "TIME"; private static final String RECORD_TOTAL = "RECORD_TOTAL"; private static final String CURRENT_PAGE = "CURRENT_PAGE"; private static final String PAGE_SIZE = "PAGE_SIZE"; //源业务系统 private static final String FROM_VALUE = "WSS"; //目标业务系统 private static final String TO_VALUE = "CRM"; //本地网ID private String wssLatnID = null; //分页处理使用的字段,默认设置为0 //总页数 private int recordTotal = 0; //当前页 private int currentPage = 0; //每页记录数数量 private int pageSize = 0; /** * 把属性组装成Map模型 */ @SuppressWarnings("unchecked") public Map marshal() { Map<String,String> map = new HashMap<String,String>(); map.put(WBConstant.WSS_LATN_ID, wssLatnID); map.put(MSG_ID, String.valueOf(System.currentTimeMillis())); map.put(SERVICE_CD, getServiceID()); map.put(FROM, FROM_VALUE); map.put(TO, TO_VALUE); map.put(TIME, String.valueOf(System.currentTimeMillis())); map.put(RECORD_TOTAL, String.valueOf(recordTotal)); map.put(CURRENT_PAGE, String.valueOf(currentPage)); map.put(PAGE_SIZE, String.valueOf(pageSize)); return map; } public abstract String getServiceID(); public int getRecordTotal() { return recordTotal; } public void setRecordTotal(int recordTotal) { this.recordTotal = recordTotal; } public int getCurrentPage() { return currentPage; } public void setCurrentPage(int currentPage) { this.currentPage = currentPage; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public String getWssLatnID() { return wssLatnID; } public void setWssLatnID(String wssLatnID) { this.wssLatnID = wssLatnID; } } 提供一个抽象类方便具体的请求类实现: public abstract class AbstractReq extends BaseReq implements MarshalEnable { //参数体标示符 private static final String CONTRACT_BODY = "contractBody"; /** * 把属性组装成Map模型 */ @SuppressWarnings("unchecked") public Map marshal() { Map map = super.marshal(); Map contractBody = new HashMap(); map.put(CONTRACT_BODY, contractBody); addValue2Map(contractBody); return map; } /** * 将req对象的值放到map中 * @param map */ @SuppressWarnings("unchecked") public abstract void addValue2Map(Map map); } 一个具体的请求对象的例子: public class ModifyCustInfoReq extends AbstractReq { // 修改标识类型:1-客户ID(默认) 2-客户卡ID private String identityType = null; // 客户ID,客户卡ID private String identityValue = null; // 客户名称 private String cusName = null; // 客户类型1 客户 2 用户 private String type = null; // 客户等级 private String custClass = null; // 区号,例如551 private String latnId = null; // 客户性别1男2女 private String sex = null; // 客户地址 private String custAddress = null; // 联系电话 private String contactNumber = null; // 证件类型编码 private String certType = null; // 证件号码 private String certCode = null; // 行业类型编码 private String industryCd = null; // 行业类型名称 private String industryName = null; // 区号 例如0551 private String areaCode = null; // 客户类型编码 private String custType = null; // 联系人 private List<ContactPersonReq> contactPersons = null; // 属性集 private List<CustomerInfoAttrReq> custModifyInfoSets = null; // 业务ID private static final String SERVICE_ID = "exePty00002"; /** * 设置业务ID 本方法设置为抽象方法是强制要求进行设置 将以使用常量 */ @Override public String getServiceID() { return SERVICE_ID; } /** * 将req对象的值放到map中 */ @Override @SuppressWarnings("unchecked") public void addValue2Map(Map map) { HashMap exePty00002 = new HashMap(); map.put(SERVICE_ID, exePty00002); exePty00002.put("identityType", identityType); exePty00002.put("identityValue", identityValue); exePty00002.put("cusName", cusName); exePty00002.put("type", type); exePty00002.put("custClass", custClass); exePty00002.put("sex", sex); exePty00002.put("latnId", latnId); exePty00002.put("custAddress", custAddress); exePty00002.put("certType", certType); exePty00002.put("certCode", certCode); exePty00002.put("industryCd", industryCd); exePty00002.put("industryName", industryName); exePty00002.put("areaCode", areaCode); exePty00002.put("custType", custType); if (contactPersons != null && contactPersons.size() > 0) { Map cpsMap = new HashMap(); exePty00002.put("contactPersons", cpsMap); Map[] persons = new HashMap[contactPersons.size()]; for (int i = 0; i < contactPersons.size(); i++) { persons[i] = contactPersons.get(i).marshal(); } cpsMap.put("contactPerson", persons); } if (custModifyInfoSets != null && custModifyInfoSets.size() > 0) { Map cmiMap = new HashMap(); exePty00002.put("custModifyInfoSets", cmiMap); Map[] infos = new HashMap[custModifyInfoSets.size()]; for (int i = 0; i < custModifyInfoSets.size(); i++) { infos[i] = custModifyInfoSets.get(i).marshal(); } cmiMap.put("custModifyInfoSet", infos); } } public static void main(String[] args) { ModifyCustInfoReq modify = new ModifyCustInfoReq(); String result = null; try { result = ReqTools.getStringOfMap(modify.marshal(), ""); } catch (Exception e) { } System.out.println(result); } public String getIdentityType() { return identityType; } public void setIdentityType(String identityType) { this.identityType = identityType; } public String getIdentityValue() { return identityValue; } public void setIdentityValue(String identityValue) { this.identityValue = identityValue; } public String getCusName() { return cusName; } public void setCusName(String cusName) { this.cusName = cusName; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getCustClass() { return custClass; } public void setCustClass(String custClass) { this.custClass = custClass; } public String getLatnId() { return latnId; } public void setLatnId(String latnId) { this.latnId = latnId; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCertType() { return certType; } public void setCertType(String certType) { this.certType = certType; } public String getCertCode() { return certCode; } public void setCertCode(String certCode) { this.certCode = certCode; } public String getIndustryCd() { return industryCd; } public void setIndustryCd(String industryCd) { this.industryCd = industryCd; } public String getIndustryName() { return industryName; } public void setIndustryName(String industryName) { this.industryName = industryName; } public String getAreaCode() { return areaCode; } public void setAreaCode(String areaCode) { this.areaCode = areaCode; } public String getCustType() { return custType; } public void setCustType(String custType) { this.custType = custType; } public List<ContactPersonReq> getContactPersons() { return contactPersons; } public void setContactPersons(List<ContactPersonReq> contactPersons) { this.contactPersons = contactPersons; } public List<CustomerInfoAttrReq> getCustModifyInfoSets() { return custModifyInfoSets; } public void setCustModifyInfoSets( List<CustomerInfoAttrReq> custModifyInfoSets) { this.custModifyInfoSets = custModifyInfoSets; } public String getContactNumber() { return contactNumber; } public void setContactNumber(String contactNumber) { this.contactNumber = contactNumber; } } 如果需要将以上的对象转化为Map只需要调用其marshal()方法即可. 在接口层使用了一个类中的2个方法分别实现Map到XML和XML到Map的转化: /** * 用于与CRM进行时HashMap 与 XML 互转 * * @author yangfeng * @version 1.0 */ public class XMLParser { private static final Logger logger = LoggerFactory .getLogger(XMLParser.class); // 系统的换行符 // static final String LINE_SEP = System.getProperty("line.separator"); private static final int XML_INIT_LENGTH = 1000; // 请求包中的行缩进 // private static final String TAB = "\t"; // XML格式符号 private static final String XML_START_LEFT = "<"; private static final String XML_RIGHT = ">"; private static final String XML_END_LEFT = "</"; private XMLParser() { } /** * 根据hashmap的模型转化为内容字符串 * * @param map * HashMap 输入的Map * @param tabs * String XML文件缩进空格 * @throws Exception * 如果HashMap中无对应类型抛出异常 * @return String 返回的XML片段 */ @SuppressWarnings("unchecked") public static String getStringOfMap(Map map, String tabs) { StringBuilder sb = new StringBuilder(XML_INIT_LENGTH); Iterator keys = map.keySet().iterator(); while (keys.hasNext()) { Object key = keys.next(); Object val = map.get(key); if (val == null) {// 没有值 sb.append(tabs); sb.append(XML_START_LEFT + key + XML_RIGHT); // sb.append(""); sb.append(XML_END_LEFT + key + XML_RIGHT); } else {// 有值 if (val instanceof String) { // 包含int sb.append(tabs); sb.append(XML_START_LEFT + key + XML_RIGHT); sb.append(val); sb.append(XML_END_LEFT + key + XML_RIGHT); } else if (val instanceof HashMap) { // 如果还有子节点,则递归调用 sb.append(tabs); sb.append(XML_START_LEFT + key + XML_RIGHT); sb.append(WBConstant.LINE_SEP); sb.append(getStringOfMap((HashMap) val, tabs + WBConstant.TAB)); sb.append(tabs); sb.append(XML_END_LEFT + key + XML_RIGHT); } else if (val instanceof HashMap[]) { // 如果还有并列的子节点数组,则递归调用 HashMap[] maps = (HashMap[]) val; for (int i = 0; i < maps.length; i++) {// 每个map一个节点 sb.append(tabs); sb.append(XML_START_LEFT + key + XML_RIGHT); sb.append(WBConstant.LINE_SEP); sb .append(getStringOfMap(maps[i], tabs + WBConstant.TAB)); sb.append(tabs); sb.append(XML_END_LEFT + key + XML_RIGHT); sb.append(WBConstant.LINE_SEP); } } else { // 出现异常记录日志,返回空 logger.error("Unkown Object in marshal map:" + val); return null; } } sb.append(WBConstant.LINE_SEP); } return sb.toString(); } /** * * @param xmlStr * @return */ @SuppressWarnings("unchecked") public static Map string2Map(String xmlStr) { if (xmlStr == null) { return null; } SAXBuilder builder = new SAXBuilder(false); Document doc = null; try { doc = builder.build(new ByteArrayInputStream(xmlStr.getBytes())); } catch (Exception e) { logger.error("xml read error!!!", e); return null; } Element root = doc.getRootElement(); return (Map) marshal2Map(root, null, null); } /** * * @param xmlStr * @return */ @SuppressWarnings("unchecked") public static Map string2Map(File xmlStr) { if (xmlStr == null) { return null; } SAXBuilder builder = new SAXBuilder(false); Document doc = null; try { doc = builder.build(new FileInputStream(xmlStr)); } catch (Exception e) { logger.error("xml read error!!!", e); return null; } Element root = doc.getRootElement(); return (Map) marshal2Map(root, null, null); } /** * 把JDOM模型解析成map模型 本方法递归调用 为了便于进行递归调用 * * @param ele * Element XML文件中的一个根节点 * @param superName * 根节点对应上一级的名称 * @param supermap * 根节点对应上一级的map * * @return HashMap */ @SuppressWarnings("unchecked") private static Object marshal2Map(Element ele, String superName, Map supermap) { Map map = new HashMap(); List children = ele.getChildren(); List slist = new ArrayList(); for (int i = 0; i < children.size(); i++) { Element child = (Element) children.get(i); String name = child.getName().trim(); int childSize = child.getChildren().size(); if (childSize == 0) {// 无下一级节点, String val = child.getText().trim(); if (slist.size() > 0) { Map temp = new HashMap(); temp.put(name, val); slist.add(temp); } else if (map.containsKey(name)) { Map temp = new HashMap(); temp.put(name, val); slist.add(map); slist.add(temp); } else { map.put(name, val); } } else {// 还有一层子节点,从根开始是第3层了, // 重名的子节点的情况只有复杂的节点才有 Object childMap = marshal2Map(child, name, map); if (childMap instanceof Map) { if (map.containsKey(name)) { if (supermap.get(superName) instanceof List) { List tempList = (List) supermap.get(superName); Map temp = new HashMap(); temp.put(name, childMap); tempList.add(temp); } else { List list = new ArrayList(); list.add(map); Map temp = new HashMap(); temp.put(name, childMap); list.add(temp); //supermap.remove(superName); supermap.put(superName, list); } } else { map.put(name, childMap); } } else { map.put(name, childMap); } } } //当多个相同节点已经转化为List放入父MAP中时候需要将其作为返回对象 if(supermap!=null){ Object obj = supermap.get(superName); if(obj!=null && obj instanceof List){ return obj; } } if (slist.size() != 0) { return slist; } return map; } @SuppressWarnings("unchecked") public static void main(String[] args) { // String path = "file:///f:/1.xml"; File file = new File("f:/document/Wss/200904/1.xml"); Map map = string2Map(file); System.out.println(map); } } 以上最为复杂的应该是方法marshal2Map,该方法使用了递归调用,返回值使用的Object,为什么呢? 主要原因是因为需要使用Map+List进行封装,递归调用的返回值可能是Map也可以能是List,所以使用 了Object作为返回值,但是最终返回给其他调用方法的是Map对象,此类处理方式是很罕见的。 希望以上的代码和说明对大家有帮助。 说明: 1.该方法对于XML文件有一个小小的要求: 如下: <abc></abc> <dex></dex> <dex></dex> <dex></dex> <dex></dex> <sf></sf> 上面这一部分需要做一个小的改动,必须改为: <abc></abc> <dexs> <dex></dex> <dex></dex> <dex></dex> <dex></dex> </dexs> <sf></sf> 就是这点要求。 2.还有一点就是对于返回的XML文,如果是多值的话,如说明1中所示, 但是实际只有1个值的话,将可能不会使用List进行封装,而是直接使用 简单的List进行封装。
