比较两个xml,如果顺序是一样的很好比对,用比对工具Beyond Compare 就可以,结果一目了然。但是如果是乱序的呢,那整个文档都是红色的。我知道有人用Python可以轻松写出这样的比对代码。但是Java就鲜有这样的资料,我是没找到。关于这个比对,我思考了很久,也挣扎了很久,今天终于是完成它的雏形。关键是思路,我一直没有想出好的办法去实现他,我也一直没动手,我知道不动手是写不出程序的,光靠想是想不出来的。
我简单描述下大体思路(我觉得思路很重要,代码可以千变万化,但需要思路指引方向):
1.主要辅助类: 我利用了dom4j xpath ,查看了官方文档中的 Fast Looping 。 把xml变成一个map。
循环遍历之后,我用了node.getParent().getUniquePath()获取每个路径,这个很神奇,他可以直达叶子节点。
2.之后,我做了处理,对list节点进行 去[] 处理。
如果list生成的路径是 :/a/b/c[1]/d 这种格式,我统一把他变成 /a/b/c,处理成一个传统的listmap。
3.开始比较。首先做一轮节点差异比较。有差异用StringBuilder接收
4.再进行值比较。
(1) 遍历map,比较list,判断map的value是不是list类型 用instanceof
如果是list类型,我先对两个list做一个消除操作,即先遍历两个list中的每一个map,检查是否有相同的map(key-value一致),如果一直就对list进行remove操作,如果两个xml一致,那么最后所有的list会全部消失,不再进行差异比较。
消除完以后,剩下有差异的list,进行比较:
三个循环(两个list和第2个list中的map),逻辑不好描述,大体就是遍历值相等就continue,如果不相等,别急着判断差异,因为是乱序的,所以可能是在第2个map中匹配上,因此要判断是否已经执行完第2个list循环,如果执行完了就是有差异。
不过之前我都做了消除的操作,这步其实也不用做,可以直接比差异了。
package com.service;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
public class CompareXMLService {
public static Document parse(File file) throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(file);
return document;
}
public static Map<String, Object> treeWalk(Document document) {
Map<String, Object> map = new TreeMap<String, Object>();
treeWalk(map, document.getRootElement());
map = dealList(map);
return map;
}
/**
*
* @describe 遍历
* @date 2019-04-04
* @param map
* @param element
* @return
*/
public static Map<String, Object> treeWalk(Map<String, Object> map, Element element) {
for (int i = 0, size = element.nodeCount(); i < size; i++) {
Node node = element.node(i);
if (node instanceof Element) {
treeWalk(map, (Element) node);
} else {
// System.out.println(node.getParent().getUniquePath());
String path = node.getParent().getUniquePath();
if (path.indexOf("response/") != -1) {
map.put(path, node.getText());
}
}
}
return map;
}
/**
*
* @describe 处理xml中的list,转化为传统list
* @date 2019-04-04
* @param map
* @return
*/
public static Map<String, Object> dealList(Map<String, Object> map) {
Map<String, Object> newMap = new TreeMap<>();
// newMap.putAll(map);
List<Map<String, Object>> rowList = null;
Map<String, Object> tmpMap = null;
String tmp1 = "";
String tmp2 = "";
String m1 = "";
String m2 = "";
for (Map.Entry<String, Object> s : map.entrySet()) {
String key = s.getKey();
tmp2 = tmp1;
if (key.indexOf('[') != -1) {
String t1 = key.substring(0, key.indexOf('['));
tmp1 = t1.substring(t1.lastIndexOf('/'));
m1 = m2;
m2 = key.substring(0, key.indexOf(']') + 1);
if (!tmp1.equals(tmp2) || rowList == null) {
rowList = new LinkedList<>();
newMap.put(t1, rowList);
}
if (!m1.equals(m2) || tmpMap == null) {
tmpMap = new TreeMap<>();
rowList.add(tmpMap);
}
// tmpMap.put(key, s.getValue());
// 把[]去掉
tmpMap.put(key.substring(0, key.indexOf('[')) + key.substring(key.indexOf(']') + 1), s.getValue());
} else {
newMap.put(key, s.getValue());
}
// System.out.println("------------" + key);
}
return newMap;
}
/**
*
* @describe 消除相同的map(key-value)
* @date 2019-04-04
* @param list1
* @param list2
*/
public static void removeList(List<Map<String, Object>> list1, List<Map<String, Object>> list2) {
// 消消乐操作 start
for (int i = 0, len = list1.size(); i < len; i++) {
Map<String, Object> map1 = list1.get(i);
boolean isEqual = false;// 是否相等标志
for (int j = 0, len2 = list2.size(); j < len2; j++) {
Map<String, Object> map2 = list2.get(j);
int k = 0;
for (Map.Entry<String, Object> entry1 : map1.entrySet()) {
String key = entry1.getKey();
String value1 = String.valueOf(entry1.getValue());
// key = key.substring(0, key.indexOf('[') + 1) + (j + 1) +
// key.substring(key.indexOf(']'));
String value2 = String.valueOf(map2.get(key));
if (value1.trim().equals(value2.trim())) {// 判断两个listmap中的每个key-value是否相等
continue;
}
k++;
}
if (k == 0) {// 如果listmap中的key-value都相等,即全部continue,那么k==0
//System.out.println("remove list2");
isEqual = true;// 存在 list1.map==list2.map
list2.remove(j);// 消除掉此map,不加入后面的差异比较
j = j - 1;
if (j == -1) {
removeList(list1, list2);
}
break;
}
}
if (isEqual) {
//System.out.println("remove list1");
list1.remove(i);// 消除掉此map,不加入后面的差异比较
i = i - 1;
if (i == -1) {
removeList(list1, list2);
}
break;
}
}
// 消消乐操作 end
}
/**
*
* @describe 比较两个list中的每个map(map中有一个值不相等即视为不相等)
* @date 2019-04-04
* @param list1
* @param list2
*/
public static StringBuilder compareMapOfList(List<Map<String, Object>> list1, List<Map<String, Object>> list2) {
// 消消乐操作 start
removeList(list1, list2);
// 消消乐操作 end
StringBuilder sb = new StringBuilder();
//System.out.println("list1=" + list1.size() + ";list2=" + list2.size());
for (int i = 0, len = list1.size(); i < len; i++) {
Map<String, Object> map1 = list1.get(i);
StringBuilder sbTmp = new StringBuilder();
for (int j = 0, len2 = list2.size(); j < len2; j++) {
Map<String, Object> map2 = list2.get(j);
boolean isDiff = false;
for (Map.Entry<String, Object> entry1 : map1.entrySet()) {
String key = entry1.getKey();
String value1 = String.valueOf(entry1.getValue());
// System.out.println("--"+key+";"+key.lastIndexOf(']')+";"+(key.length()-1));
// key = key.substring(0, key.indexOf('[') + 1) + (j + 1) +
// key.substring(key.indexOf(']'));
String value2 = String.valueOf(map2.get(key));
if (value1.trim().equals(value2.trim())) {
continue;
} else {
// System.out.println("----比对过程存在不相同" + key + value1 +
// value2);
isDiff = true;
sbTmp.append("[该list 中存在值差异]:" + key + ";value=" + value1 + "/" + value2 + "\r\n");
if (j == len2 - 1) {
sb.append(sbTmp);
}
break;
}
}
if (!isDiff) {
break;
}
}
}
return sb;
}
/**
*
* @describe 比较两个xml
* @date 2019-04-04
* @param doc
* @param doc1
* @return
*/
public StringBuilder compareDoc(Document doc, Document doc1) {
Map<String, Object> map1 = treeWalk(doc);
Map<String, Object> map2 = treeWalk(doc1);
Map<String, Object> map = new TreeMap<String, Object>();
map.putAll(map1);
map.putAll(map2);
StringBuilder sb = new StringBuilder();
// 判断节点差异
for (Map.Entry<String, Object> s : map.entrySet()) {
if (!map1.containsKey(s.getKey()) || !map2.containsKey(s.getKey())) {
sb.append("[节点差异]:" + s.getKey() + "\r\n");
}
}
Object value1 = null;
Object value2 = null;
// 判断值差异
for (Map.Entry<String, Object> s : map1.entrySet()) {
String key = s.getKey();
value1 = s.getValue();
value2 = map2.get(key);
if (value1 instanceof List) {
sb.append(compareMapOfList((List<Map<String, Object>>) value1, (List<Map<String, Object>>) value2));
} else {
if (value2 != null && !value2.toString().equals(value1.toString())) {
sb.append("[值差异]:" + key + " value:" + value1 + "/" + value2 + "\r\n");
}
}
}
return sb;
}
/**
*
* @describe 比较两个xml
* @date 2019-04-04
* @param text1
* @param text2
* @return
*/
public String compareXml(String text1, String text2) {
StringBuilder sb = new StringBuilder();
Document doc = null;
try {
doc = DocumentHelper.parseText(text1);
} catch (DocumentException e1) {
e1.printStackTrace();
sb.append(e1.getMessage());
}
Document doc1 = null;
try {
doc1 = DocumentHelper.parseText(text2);
} catch (DocumentException e) {
e.printStackTrace();
sb.append(e.getMessage());
}
if (sb.length() == 0) {
sb.append(compareDoc(doc, doc1));
}
if (sb.length() == 0) {
sb.append("结果一致!");
}
//System.out.println(sb.toString());
return sb.toString();
}
public static void main(String[] args) throws DocumentException {
Document doc = parse(new File("C:/Users/Administrator/Desktop/3.txt"));
Document doc1 = parse(new File("C:/Users/Administrator/Desktop/4.txt"));
new CompareXMLService().compareXml(doc.asXML(), doc1.asXML());
}
}
因为我做成了swing界面,界面传String类型,所以再转了一次 doc.asXML();
xml:
<?xml version="1.0" encoding='utf-8'?>
<a>
<response>
<b>
<c>1</c>
<d>d1</d>
</b>
<b>
<c>2</c>
<d>d1</d> 另一个报文这边改一下数据
</b>
</response>
</a>
[该list 中存在值差异]:/a/response/b/d;value=d1/d1222