xml/map转换器

  xml转换: xml/map转换器

  xml合并: xml合并

  snagit图片:http://pan.baidu.com/s/1nuKJD13

  git样例: https://gitee.com/KingBoBo/XmlMapBeanTool/tree/master/XmlMapBeanTool

应用场景,为什么要把xml转map?我直接用jdom,dom4j操作不行吗?

  • 如果你了解模板引擎(像velocity,mvel,httl等),会发现map形式在模板中取数可以如下所示直接取值,而xml字符串或dom则没有这样取值的便利和易理解特性.
<爸爸的小狗毛色>PACKET.MASTER_LIST.MASTER[].DOG.COLOR</爸爸的小狗毛色>

  以上语句中文翻译为取得PACKET报文中的第2个MASTER主人拥有的狗狗的毛色.

  • 而map.get("key")取值的性能远胜于其它方式,针对报文中部分指定节点做类似于md5的重复性校验(查重)也非常合适.

比如我们在库中指定以下节点作为查重节点:

节点中文名
关键节点(查重节点)
地址 PACKET/FAMILY/ADDESS
主人名 PACKET/MASTER_LIST/MASTER/NAME

之后可以通过以下方式取值

map.get("PACKET").get("FAMILY").get("ADDRESS") ;

map.get("PACKET").get("MASTER_LIST").get("MASTER").get("NAME"); // 考虑到有list,在取到get("MASTER")时做instanceof LIST,之后遍历取出3个NAME

再把它们各自的value拼成 "绍兴兰亭MamaBabaSon",  最终后续来的报文也提取类似"杭州江干MamaBabaDaughter"进行历史比对,就能马上发现存在差异,至于有没有差异的后续处理得根据业务需要了. (为了更高的性能先用length,再用equals,该点很重要,属于性能优化点.)

设计思路

1. 有上下层次关系,必然递归最适合
2. 递归原则 : 一个map对应一个xml节点,key为当前xml节点名,value为当前xml节点子集
3.1 如果xml节点没有子节点(叶子节点),那么map的key为xml节点名,value为xml节点文本内容
3.2 如果xml节点有一个子节点,那么map的key为xml节点名,value为xml节点子集
3.3.1 如果xml节点有多个子节点,对应map的key不存在(每一次),map的key为xml节点名,value为xml节点子集
3.3.2 如果xml节点有多个子节点,对应map的key已存在,且value为map类型(第二次),map的key为xml节点名,值从map类型转为list,而list中添加2份当前xml节点子集
3.3.3 如果xml节点有多个子节点,对应map的key已存在,且value为list类型(第三/多次),那么直接加到list中去.

原始xml:

<?xml version="1.0" encoding="utf-8"?>
<PACKET>
<FAMILY>
<COLOR>red</COLOR>
<ADDRESS>绍兴兰亭</ADDRESS>
</FAMILY>
<MASTER_LIST>
<MASTER>
<NAME>Mama</NAME>
<CAT>
<COLOR>yellow</COLOR>
</CAT>
<CAT>
<COLOR>grey</COLOR>
</CAT>
</MASTER>
<MASTER>
<NAME>Baba</NAME>
<DOG>
<COLOR>black</COLOR>
</DOG>
</MASTER>
<MASTER>
<NAME>Son</NAME>
<RABBIT>
<COLOR>white</COLOR>
</RABBIT>
</MASTER>
</MASTER_LIST>
</PACKET>

期望map:

{
PACKET={
FAMILY={
ADDRESS=绍兴兰亭,
COLOR=red
},
MASTER_LIST={
MASTER=[
{
NAME=Mama,
CAT=[
{
COLOR=yellow
},
{
COLOR=grey
}
]
},
{
NAME=Baba,
DOG={
COLOR=black
}
},
{
NAME=Son,
RABBIT={
COLOR=white
}
}
]
}
}
}

图文思路解析

总map结构

叶子节点map结构

list层次结构第一次生成时

list层次结构第二次生成时

list层次结构第三/多次生成时

初版原码

先理解初版,最重要版本.

package test.king;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element; import test.king.tool.FileTool; public class XmlMapTool {
public static void main(String[] args) {
String input = FileTool.readStringFromFile("d://input.txt", "gbk");
Map<String, Object> map = xml2map(input);
System.out.println("最终生成的map如下:\n"+map);
} public static Map<String, Object> xml2map(String xml) {
Document doc = null;
try {
doc = DocumentHelper.parseText(xml);
} catch (DocumentException e) {
e.printStackTrace();
}
Map<String, Object> map = new HashMap<String, Object>();
if (doc == null)
return map;
Element rootElement = doc.getRootElement();
element2map(rootElement,map);
return map;
} public static Map element2map (Element outele,Map outmap) {
System.out.println("当前位于"+outele.getName()+"节点");
List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不会异常
int size = list.size();
if(size == 0){//当前节点是叶子节点
outmap.put(outele.getName(), outele.getTextTrim());
}else if(size == 1){
Map<String, Object> innermap = new HashMap<String, Object>();
Element ele1 = list.get(0);
element2map(ele1,innermap);
outmap.put(outele.getName(), innermap);
}else if(size > 1){
Map<String, Object> innermap = new HashMap<String, Object>();
for(Element ele1 : list){
String eleName = ele1.getName();
Object obj = innermap.get(eleName);//获取MASTER
if(obj == null){//如果该MASTER不存在,现在有一个MASTER过来
element2map(ele1,innermap);
}else{
if(obj instanceof java.util.Map){//如果没有生成过list,把原来的单个map合并到新的list
innermap.remove(eleName);
List<Map> list1 = new ArrayList<Map>();
list1.add((Map) obj);
Map<String, Object> map1 = new HashMap<String, Object>();
element2map(ele1,map1);
list1.add((Map) map1.get(eleName));
innermap.put(eleName, list1);
}else if(obj instanceof java.util.List){//如果已经生成过list
element2map(ele1,innermap);
((List)obj).add(innermap);
}
}
}
outmap.put(outele.getName(), innermap);
}
return outmap;
} }

优化后原码

最终发现else if( size == 1 ){...... }的功能在 else if( size >1 ) 中已完全包含,所以可以舍掉 (size == 1) 这个多余处理.

且这里没有使用Iterator<Element> eleItor = outele.elementIterator();用法,因为亲自经过10000次大报文自测发现还是size()判断更快.

size用法(推荐):

package test.king;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element; import test.king.tool.FileTool;
import test.king.tool.TLTimeContainer; public class XmlMapToolIngenious {
public static void main(String[] args) {
String input = FileTool.readStringFromFile("d://input.txt", "gbk");
Map<String, Object> map = null;
TLTimeContainer.recordTime();
for(int i = 0 ; i < 10000 ; i++){
map = xml2map(input);
}
TLTimeContainer.recordTime();
TLTimeContainer.print();
System.out.println("最终生成的map如下:\n"+map);
} public static Map<String, Object> xml2map(String xml) {
Document doc = null;
try {
doc = DocumentHelper.parseText(xml);
} catch (DocumentException e) {
e.printStackTrace();
}
Map<String, Object> map = new HashMap<String, Object>();
if (doc == null)
return map;
Element rootElement = doc.getRootElement();
element2map(rootElement,map);
return map;
} public static Map element2map (Element outele,Map outmap) {
List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不会异常
int size = list.size();
if(size == 0){//当前节点是叶子节点(outele如果为叶子节点,是不可能有子节点的,因为它里面是纯文本)
outmap.put(outele.getName(), outele.getTextTrim());
}else{
Map<String, Object> innermap = new HashMap<String, Object>();
for(Element ele1 : list){
String eleName = ele1.getName();
Object obj = innermap.get(eleName);//获取MASTER
if(obj == null){//如果该MASTER不存在,现在有一个MASTER过来
element2map(ele1,innermap);
}else{
if(obj instanceof java.util.Map){//如果没有生成过list,把原来的单个map合并到新的list
List<Map> list1 = new ArrayList<Map>();
list1.add((Map) innermap.remove(eleName));
element2map(ele1,innermap);
list1.add((Map) innermap.remove(eleName));
innermap.put(eleName, list1);
}else{//如果不是map,必然是list,只有这两种情况,所以不再else if 条件判断
element2map(ele1,innermap);
((List)obj).add(innermap);
}
}
}
outmap.put(outele.getName(), innermap);
}
return outmap;
} }

同事施明方法,比我的更优雅更简洁更高效(强烈推荐)

1. 有上下层次关系,必然递归最适合
2. 递归原则 : 一个map对应一个xml节点,key为当前xml节点名,value为当前xml节点子集
3. 如果xml节点没有子节点(叶子节点),那么map的key为xml节点名,value为xml节点内文本内容
4. 如果xml节点有子节点,那么让子节点和新生成子map去递归(子节点和新map会关联到一起)
5. 然后就要把子map或子list置入上一层map中
5.2如果子map第一次生成,用上层map直接添加子map
5.3如果子map第二次生成,用新list把两份子map添加,再用上一层map添加新list
5.4如果子map第三/多次,用list把第三/多次子map直接添加

public void element2map(Element elmt, Map<Object, Object> map) {
if (null == elmt) {
return;
}
String name = elmt.getName();
if (elmt.isTextOnly()) {
map.put(name, elmt.getText());
} else {
Map<Object, Object> mapSub = new HashMap<Object, Object>();
List<Element> elements = (List<Element>) elmt.elements();
for (Element elmtSub : elements) {
element2map(elmtSub, mapSub);
}
Object first = map.get(name);
if (null == first) {
map.put(name, mapSub);
} else {
if (first instanceof List<?>) {
((List) first).add(mapSub);
} else {
List<Object> listSub = new ArrayList<Object>();
listSub.add(first);
listSub.add(mapSub);
map.put(name, listSub);
}
}
}
}

 微整后

/**
*
* @param elmt 当前元素
* @param map 主键为当前元素的节点名,值为当前元素的所有直接子元素
*/
public static void element2map(Element elmt, Map<String, Object> map) {
if (null == elmt) {
return;
}
String name = elmt.getName();
if (elmt.isTextOnly()) {
map.put(name, elmt.getText());
} else {
Map<String, Object> mapSub = new HashMap<String, Object>();
List<Element> elements = (List<Element>) elmt.elements();
for (Element elmtSub : elements) {
element2map(elmtSub, mapSub);
}
Object first = map.get(name);
if (null == first) {
map.put(name, mapSub);
} else {
if (first instanceof List<?>) {
((List) first).add(mapSub);
} else {
List<Object> listSub = new ArrayList<Object>();
listSub.add(first);
listSub.add(mapSub);
map.put(name, listSub);
}
}
}
}

iterator用法(不推荐):

因为在10000次循环时间对比中,还没有size()判断比iterator()快1秒左右. 起码在dom4j中是这样的情况.

package test.king;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element; import test.king.tool.FileTool;
import test.king.tool.TLTimeContainer; public class XmlMapToolIngenious {
public static void main(String[] args) {
String input = FileTool.readStringFromFile("d://input.txt", "gbk");
Map<String, Object> map = null;
TLTimeContainer.recordTime();
for(int i = 0 ; i < 10000 ; i++){
map = xml2map(input);
}
TLTimeContainer.recordTime();
TLTimeContainer.print();
System.out.println("最终生成的map如下:\n"+map);
} public static Map<String, Object> xml2map(String xml) {
Document doc = null;
try {
doc = DocumentHelper.parseText(xml);
} catch (DocumentException e) {
e.printStackTrace();
}
Map<String, Object> map = new HashMap<String, Object>();
if (doc == null)
return map;
Element rootElement = doc.getRootElement();
element2map(rootElement,map);
return map;
} public static Map element2map (Element outele,Map outmap) {
List<Element> list = outele.elements();//必定返回0,1,2,3,....... 不会异常
int size = list.size();
Iterator<Element> eleItor = outele.elementIterator();
if(!eleItor.hasNext()){//当前节点是叶子节点(outele如果为叶子节点,是不可能有子节点的,因为它里面是纯文本)
outmap.put(outele.getName(), outele.getTextTrim());
}else{
Map<String, Object> innermap = new HashMap<String, Object>();
for(;eleItor.hasNext();){
Element ele1 = eleItor.next();
String eleName = ele1.getName();
Object obj = innermap.get(eleName);//获取MASTER
if(obj == null){//如果该MASTER不存在,现在有一个MASTER过来
element2map(ele1,innermap);
}else{
if(obj instanceof java.util.Map){//如果没有生成过list,把原来的单个map合并到新的list
List<Map> list1 = new ArrayList<Map>();
list1.add((Map) innermap.remove(eleName));
element2map(ele1,innermap);
list1.add((Map) innermap.remove(eleName));
innermap.put(eleName, list1);
}else{//如果不是map,必然是list,只有这两种情况,所以不再else if 条件判断
element2map(ele1,innermap);
((List)obj).add(innermap);
}
}
}
outmap.put(outele.getName(), innermap);
}
return outmap;
} }

相关类

FileTool.java工具类  (用于读取文件内容)

备用


//     IteRator<Element> eles = root.elementIterator();
// while(eles.hasNext()){
// // }

本文纯原创,开源精神,欢迎转载,请说明出处 by 金墨痴 http://www.cnblogs.com/whatlonelytear/p/5797979.html

xml/map转换器,递归设计思路【纯原】的更多相关文章

  1. xml/map转换器,递归设计思路

    xml/map转换器 图片:http://pan.baidu.com/s/1nuKJD13 应用场景,为什么要把xml转map?我直接用jdom,dom4j操作不行吗? 如果你了解模板引擎(像velo ...

  2. druid 源码分析与学习(含详细监控设计思路的彩蛋)(转)

    原文路径:http://herman-liu76.iteye.com/blog/2308563  Druid是阿里巴巴公司的数据库连接池工具,昨天突然想学习一下阿里的druid源码,于是下载下来分析了 ...

  3. 7.地图随机装饰,与转化过程补充,与ai的设计思路

    这两天本来只想实现地图的随机装饰,然后发现以前的bin格式设计存在不足,所以最后不得不去改地图,并去重制整个地图的阶段,此篇总结这个过程 先描述下bin结构 首先地图由无数六边形组合,一个六边形由两层 ...

  4. asp.net webform设计思路的思考

    我使用asp.net的webform框架进行web应用程序的开发已经差不多四年了,在整个开发生涯中,也使用过一年asp.net的mvc框架.因为网上经常有讨论webform框架和mvc框架的优劣,所以 ...

  5. Backbone设计思路和关键源码分析

    一. Backbone的江湖地位: backbone作为一个老牌js框架为大规模前端开发提供了新的开发思路:前端MVC模式,这个模式也是前端开发演变过程中的一个重要里程碑,也为MVVM和Redux等开 ...

  6. jMiniLang设计思路

    前言 项目地址:https://github.com/bajdcc/jMiniLang 演示视频:https://www.bilibili.com/video/av13294962 jMiniLang ...

  7. 分享一个CQRS/ES架构中基于写文件的EventStore的设计思路

    最近打算用C#实现一个基于文件的EventStore. 什么是EventStore 关于什么是EventStore,如果还不清楚的朋友可以去了解下CQRS/Event Sourcing这种架构,我博客 ...

  8. MVC3 数据验证用法之密码验证设计思路

    描述:MVC数据验证使用小结 内容:display,Required,stringLength,Remote,compare,RegularExpression 本人最近在公司用mvc做了一个修改密码 ...

  9. ENode 1.0 - 消息的重试机制的设计思路

    项目开源地址:https://github.com/tangxuehua/enode 上一篇文章,简单介绍了enode框架中消息队列的设计思路,本文介绍一下enode框架中关系消息的重试机制的设计思路 ...

随机推荐

  1. 圆桌的项目Alpha冲刺(团队)

    (a) 项目课堂演示 (b) 10篇冲刺随笔 冲刺之一 冲刺之二 冲刺之三 冲刺之四 冲刺之五 冲刺之六 冲刺之七 冲刺之八 冲刺之⑨ 冲刺之十 (c) 1篇测试随笔 测试随笔 (d) 1篇冲刺总结随 ...

  2. shell脚本--逻辑判断与字符串比较

    涉及到比较和判断的时候,要注意 整数比较使用-lt,-gt,ge等比较运算符,详情参考:整数比较 文件测试使用 -d, -f, -x等运算发,详情参考:文件测试 逻辑判断使用    && ...

  3. JavaScript 作用域链与闭包

    作用域链 在某个作用域访问某个变量或者函数时,会首先在自己的局部环境作用域中搜寻变量或者函数,如果本地局部环境作用域中有该变量或者函数,则就直接使用找到的这个变量值或者函数:如果本地局部环境作用域中没 ...

  4. Selenium vs TestStudio,Selenium Grid vs F2Test

    Selenium vs TestStudio,不知道差异在哪里? Selenium Grid vs F2Test,后者更优. 用Selenium + FireFox做了一个单机抓图,想要扩展成集群的话 ...

  5. Angular 简单的Get

    <!DOCTYPE html><html ng-app="myApp"><head lang="en"> <meta ...

  6. centos7黑客帝国装逼

    黑客帝国既视感 搜 cmatrix 然后放到本地解压缩 ,安装 yum install ncurses-devel./configure && make && make ...

  7. 牛客网练习赛7-B-购物

    在遥远的东方,有一家糖果专卖店. 这家糖果店将会在每天出售一些糖果,它每天都会生产出m个糖果,第i天的第j个糖果价格为C[i][j]元. 现在的你想要在接下来的n天去糖果店进行选购,你每天可以买多个糖 ...

  8. python----面对对象三大特征2

    多态 什么是多态?多态指的是一类事物有多种形态,例如一个动物类,狗和猫都通过继承动物类来得来,这样我们可以将狗.猫称作动物类的另一种形态. 在java中也存在多态,java中的多态是用来解决在一个函数 ...

  9. BZOJ3862Little Devil I——树链剖分+线段树

    题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...

  10. Codeforces Round #381 (Div. 2)C Alyona and mex

    Alyona's mother wants to present an array of n non-negative integers to Alyona. The array should be ...