java比较两个乱序xml

   比较两个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 treeWalk(Document document) {
		Map map = new TreeMap();
		treeWalk(map, document.getRootElement());
		map = dealList(map);
		return map;
	}
	/**
	 * 
	 * @describe 遍历
	 * @date 2019-04-04
	 * @param map
	 * @param element
	 * @return
	 */
	public static Map treeWalk(Map 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 dealList(Map map) {
		Map newMap = new TreeMap<>();
		// newMap.putAll(map);
		List> rowList = null;
		Map tmpMap = null;
		String tmp1 = "";
		String tmp2 = "";
		String m1 = "";
		String m2 = "";
		for (Map.Entry 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> list1, List> list2) {
		// 消消乐操作 start
		for (int i = 0, len = list1.size(); i < len; i++) {
			Map map1 = list1.get(i);
			boolean isEqual = false;// 是否相等标志
			for (int j = 0, len2 = list2.size(); j < len2; j++) {
				Map map2 = list2.get(j);
				int k = 0;
				for (Map.Entry 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> list1, List> 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 map1 = list1.get(i);
			StringBuilder sbTmp = new StringBuilder();
			for (int j = 0, len2 = list2.size(); j < len2; j++) {
				Map map2 = list2.get(j);
				boolean isDiff = false;
				for (Map.Entry 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 map1 = treeWalk(doc);
		Map map2 = treeWalk(doc1);
		Map map = new TreeMap();
		map.putAll(map1);
		map.putAll(map2);
		StringBuilder sb = new StringBuilder();
		// 判断节点差异
		for (Map.Entry 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 s : map1.entrySet()) {
			String key = s.getKey();
			value1 = s.getValue();
			value2 = map2.get(key);
			if (value1 instanceof List) {
				sb.append(compareMapOfList((List>) value1, (List>) 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:



 
   
      1
      d1
    
    
      2
      d1   另一个报文这边改一下数据
   
  

[该list 中存在值差异]:/a/response/b/d;value=d1/d1222

 

你可能感兴趣的:(xml)