论坛首页 入门技术论坛

一种Java对象与XML文互转的方法!

浏览 10557 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (10) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-05-22  

      目前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进行封装。

   发表时间:2009-05-22  
用 JSON 吧
0 请登录后投票
   发表时间:2009-05-22  
在这个项目中JSON主要用在Web层的ajax上,至于系统内部以及多个系统交互还没有考虑到使用JSON,不过倒是可以试试。使用这个方式主要是对于java程序员,MAP和LIST是大家熟知并且最易处理的数据对象。
0 请登录后投票
   发表时间:2009-05-23  
写了一堆垃圾代码。
0 请登录后投票
   发表时间:2009-05-24  
哎...我自己写了一个,如果我能早点知道XStream,我就不会自己去写了。
找个时间对比下我和XStream各有什么优劣,然后想办法参与进去
0 请登录后投票
   发表时间:2009-05-24  
这样写 数据量大的话吃不消的
我用过这样的,但前提是数据量在可控范围内
0 请登录后投票
   发表时间:2009-05-24   最后修改:2009-05-24
如果是一大堆巨型的XML对帐文件,你也不会用dom4j了,估计会用SAX。大数据量自有大数据量的处理方法,这种应该都是针对中小型的需求的。
0 请登录后投票
   发表时间:2009-05-24  
尝试下json  Hessian

可以看下这个 里面给了好多意见

http://www.iteye.com/topic/228453
0 请登录后投票
   发表时间:2009-05-24  
我们没得选的哦,接口规范中定义的是XML
0 请登录后投票
   发表时间:2009-05-25  
汗,我做到的xtream都做到了,而且比我的更好...性能也没他的高...汗一下
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics