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