判断两个XML文件结构与内容是否相同
1. 引入
目前公司的这款软件导入导出数据库信息的方法是:组织数据的内容和结构 利用MS com的sax解析 储存数据为XML格式
优点是可以选择部分导出
缺点是速度慢文件导出的文件庞大,若客户出现导入导出错误要重现或者调试困难
由于软件持续更新所以不同版本的,XML有差异
现客户需求将高版本的软件导出成 低版本XML,保证从任何高版本导出的文件 与 低版本 原先的格式与内容一致
我们实现了功能之后为了测试功能,要写单元测试
2. 分析
要测试高版本导出的文件的 内容和格式能完全导出成低版本
首先要通过低版本 生成一个数据库,将内容全部导出成A
再将A导入到高版本确保A的信息完全转化成高版本的结构
将高版本导出成低版本文件B
比较AB的内容和结构
B除某些少部分信息与结构,其他必须完全与低版本一致
3. 实现
将A与B通过 dom 解析,取出各自的节点进行一一比较直到所有在A中的节点的位置和信息都与B中一致后,才可判定2者一致,由于导出时数据库的结构不同,XML的文件中节点的顺序可能不同在我们的软件中这种情况是可以认为是一致的。顺序不用考虑,因此要求UT忽略顺序
比较主要分为3步
1)在A和B中取出某个节点
2)比较两个节点的信息是否一致
3)比较这两个节点的子节点信息是否一致
XML格式为树状结构,除根节点和叶子节点外每个节点均有父节点和子节点,另外本软件导出的文件只会有一个根节点
因此可以进行递归查找进一步把流程改为:
1)从A和B中取出根节点,对根节点进行compare(若一致则可以确定两文件内容、结构一致)
2)compare流程
(1)比较当前节点信息若不一致则返回失败
(2)若一致,则取出AB所有的子节点,遍历A的子节点 去 B的子节点集中查找 (递归调用Compare进行判断)若所有A的子节点都能在B中找到,则判断AB节点相同(前提AB中子节点数量一致)
#include "ModelParser.h"
#include "IExporterDocumentTranslator.h"
.....将低版本文件导入到高版本
.....导出成低版本
ModelParser version4Parser(fileOfversion4);//ModelParser类用来解析XML文件
ModelParser exportedFileParser(exportedFile);
ASSERT(version4Parser.IsValid());
ASSERT(exportedFileParser.IsValid());
//Compare it with the 4.0 file, their datas should be consistent
ASSERT(version4Parser.Compare(exportedFileParser));
ModelParser类及实现
ModelParser.h
#pragma once
#include"XMLDOMNodeParser.h"
class ModelParser
{
public:
ModelParser(const String& modelFilePath);
/////////////////////////////////////////////////////////////////////////
// Compare the content with another file, if the files are the same return true (exportas test use only)
bool Compare(const ModelParser& otherModel) const;
bool IsValid() const;
private:
shared_ptr<XMLDOMNodeParser> rootInterchangeFile;
IXMLDOMNodePtr GetInterchangeFile(IXMLDOMNodeListPtr listPtr);
};
ModelParser.cpp
#include "StdAfx.h"
#include "ModelParser.h"
ModelParser::ModelParser(const String& modelFilePath) :
rootInterchangeFile(NULL)
{
IXMLDOMDocumentPtr docPtr;
docPtr.CreateInstance(__uuidof(DOMDocument30));
// Load a document:
_variant_t tempPath = static_cast<LPCTSTR>(modelFilePath);
VARIANT path = tempPath;
VARIANT_BOOL result = VARIANT_FALSE;
docPtr->load(path, &result);
IXMLDOMNodeListPtr rootNodes = NULL;
const BSTR allTags = L" ";
docPtr->getElementsByTagName(allTags, &rootNodes);
IXMLDOMNodePtr root = GetInterchangeFile(rootNodes);
rootInterchangeFile = shared_ptr<XMLDOMNodeParser>(new XMLDOMNodeParser(root));
}
bool ModelParser::IsValid() const
{
return rootInterchangeFile->IsValid(*rootInterchangeFile);
}
IXMLDOMNodePtr ModelParser::GetInterchangeFile(IXMLDOMNodeListPtr listPtr)
{
IXMLDOMNodePtr nodePtr = NULL;
long num = 0;
do
{
listPtr->get_item(num++, &nodePtr);
if (nodePtr)
{
BSTR nodeType, nodeName;
nodePtr->get_nodeTypeString(&nodeType);
nodePtr->get_nodeName(&nodeName);
if (0 == (lstrcmp((LPCTSTR)nodeType, (LPCTSTR)L"element")) && 0 == (lstrcmp((LPCTSTR)nodeName, (LPCTSTR)L"InterchangeFile")))
{
break;
}
}
} while (nodePtr);
return nodePtr;
}
bool ModelParser::Compare(const ModelParser& otherModel) const
{
bool isSame = false;
try
{
isSame = rootInterchangeFile->Compare(*otherModel.rootInterchangeFile);
}
catch (...)
{
isSame = false;
}
return isSame;
}
#pragma once
#include "Strings.h"
#include <msxml2.h>
using namespace std;
struct BSTRSorter
{
bool operator()(BSTR left, BSTR right) const
{
return lstrcmp((LPCTSTR)left, (LPCTSTR)right) < 0;
}
};
class XMLDOMNodeParser
{
public:
XMLDOMNodeParser(IXMLDOMNodePtr nodePtr);
bool IsValid(const XMLDOMNodeParser& node) const;
/////////////////////////////////////////////////////////////////////////
// Compare the structure, content with another node, if the nodes are the same return true (exportas test use only)
bool Compare(XMLDOMNodeParser& otherNode);
private:
bool CompareChildren(XMLDOMNodeParser& otherNode);
bool CompareAttributes(const XMLDOMNodeParser& otherNode);
BSTR GetAttributeValueByName(const IXMLDOMNamedNodeMapPtr& attributes, const BSTR attributeName) const;
bool PutNodesAttributesIntoMap(map<BSTR, BSTR, BSTRSorter>& attributesMap, const IXMLDOMNamedNodeMapPtr& nodesPtr);
bool ShouldIgnore() const;
bool IsBuiltInGuid(const BSTR value) const;
bool IsBuiltInPresentation() const;
/////////////////////////////////////////////////////////////////////////
// Ignore some attributes' diffrence
static void InitialAttributesExcludedList();
/////////////////////////////////////////////////////////////////////////
// If a presentation's type is build-in, ignore the presentation's diffrence
static void InitialPresentationList();
IXMLDOMNodePtr node;
BSTR nodeName;
BSTR nodeType;
map<BSTR, BSTR, BSTRSorter> attributesMap;
long attributeLenth, childListLenth;
IXMLDOMNamedNodeMapPtr attributesPtr;
shared_ptr<XMLDOMNodeParser> childNodeTemp;
IXMLDOMNodeListPtr childList;
static set<BSTR, BSTRSorter> attributesExcludedList;
static set<BSTR, BSTRSorter> presentationBuildinList;
};
#include "StdAfx.h"
#include "XMLDOMNodeParser.h"
set<BSTR, BSTRSorter> XMLDOMNodeParser::attributesExcludedList;
set<BSTR, BSTRSorter> XMLDOMNodeParser::presentationBuildinList;
XMLDOMNodeParser::XMLDOMNodeParser(IXMLDOMNodePtr nodePtr) :
attributeLenth(0),
childListLenth(0),
attributesPtr(NULL),
node(NULL),
childList(NULL)
{
InitialAttributesExcludedList();
InitialPresentationList();
node = nodePtr;
if (NULL != node)
{
node->get_nodeName(&nodeName);
node->get_attributes(&attributesPtr);
if (NULL != attributesPtr)
{
attributesPtr->get_length(&attributeLenth);
if (!PutNodesAttributesIntoMap(attributesMap, attributesPtr))
node = NULL;
}
node->get_childNodes(&childList);
if(NULL != childList)
childList->get_length(&childListLenth);
node->get_nodeTypeString(&nodeType);
}
}
bool XMLDOMNodeParser::IsValid(const XMLDOMNodeParser& node) const
{
return node.node != NULL;
}
bool XMLDOMNodeParser::CompareAttributes(const XMLDOMNodeParser& otherNode)
{
if (NULL == attributesPtr || NULL == otherNode.attributesPtr || attributeLenth != otherNode.attributeLenth)
return false;
for (map<BSTR, BSTR, BSTRSorter>::iterator iter = attributesMap.begin(); iter != attributesMap.end(); ++iter)
{
BSTR attrName = iter->first;
BSTR attrText = iter->second;
if (0 == wcslen(attrName))
{
return false;
}
if (attributesExcludedList.find(attrName) != attributesExcludedList.end())
{
continue;
}
map<BSTR, BSTR, BSTRSorter>::iterator otherIter = const_cast<XMLDOMNodeParser&>(otherNode).attributesMap.find(iter->first);
if (otherIter != otherNode.attributesMap.end())
{
if (0 != wcscmp(otherIter->second, attrText))
return false;
}
else
return false;
}
return true;
}
bool XMLDOMNodeParser::ShouldIgnore() const
{
return NULL == node || 0 != lstrcmp((LPCTSTR)nodeType, (LPCTSTR)L"element");
}
bool XMLDOMNodeParser::IsBuiltInPresentation() const
{
if (0 == wcscmp(nodeName, L"PRESENTATION"))
{
BSTR owner = GetAttributeValueByName(this->attributesPtr, L"Owner");
if (IsBuiltInGuid(owner))
return true;
}
return false;
}
BSTR XMLDOMNodeParser::GetAttributeValueByName(const IXMLDOMNamedNodeMapPtr& attributes, const BSTR attributeName) const
{
IXMLDOMNodePtr pIAttrNode = NULL;
BSTR tmpName, attributeValue;
long length = 0;
attributes->get_length(&length);
for (long num = 0; num < length; ++num)
{
attributes->get_item(num, &pIAttrNode);
pIAttrNode->get_nodeName(&tmpName);
if (0 == wcscmp(tmpName, attributeName))
{
pIAttrNode->get_text(&attributeValue);
}
}
return attributeValue;
}
bool XMLDOMNodeParser::PutNodesAttributesIntoMap(map<BSTR, BSTR, BSTRSorter>&attributesMap, const IXMLDOMNamedNodeMapPtr&nodesPtr)
{
long length = 0;
nodesPtr->get_length(&length);
for (long num = 0; num < length; ++num)
{
BSTR attrName, attrText;
IXMLDOMNodePtr pIAttrNode = NULL;
nodesPtr->get_item(num, &pIAttrNode);
pIAttrNode->get_nodeName(&attrName);
pIAttrNode->get_text(&attrText);
if (0 != wcslen(attrName))
{
if (attributesExcludedList.find(attrName) == attributesExcludedList.end())
attributesMap[attrName] = attrText;
}
else
{
return false;
}
}
return true;
}
bool XMLDOMNodeParser::Compare(XMLDOMNodeParser& otherNode)
{
if (0 != wcscmp(nodeName, otherNode.nodeName))
return false;
if (this->IsBuiltInPresentation() && otherNode.IsBuiltInPresentation())
{
return true;
}
if (!CompareAttributes(otherNode))
{
return false;
}
return CompareChildren(otherNode);
}
bool XMLDOMNodeParser::CompareChildren(XMLDOMNodeParser& otherNode)
{
if (NULL == childList && NULL == otherNode.childList)//No Child
return true;
if (childListLenth != otherNode.childListLenth)
return false;
set<long> checked;
IXMLDOMNodePtr childNodeTempPtr = NULL;
for (long childnumA = 0; childnumA < childListLenth; ++childnumA)
{
childList->get_item(childnumA, &childNodeTempPtr);
childNodeTemp = shared_ptr<XMLDOMNodeParser> (new XMLDOMNodeParser(childNodeTempPtr));
if (childNodeTemp->ShouldIgnore())
continue;
bool found = false;
for (long childnumB = 0; childnumB < childListLenth; ++childnumB)
{
if (checked.find(childnumB) == checked.end())
{
otherNode.childList->get_item(childnumB, &childNodeTempPtr);
otherNode.childNodeTemp = shared_ptr<XMLDOMNodeParser> (new XMLDOMNodeParser(childNodeTempPtr));
if (otherNode.childNodeTemp->ShouldIgnore())
continue;
if (childNodeTemp->Compare(*otherNode.childNodeTemp))
{
found = true;
checked.insert(childnumB);
break;
}
}
}
if (!found)
return false;
}
return true;
}
bool XMLDOMNodeParser::IsBuiltInGuid(const BSTR value) const
{
return presentationBuildinList.find(value) != presentationBuildinList.end();
}
/////////////////////////////////////////////////////////////////////////
// Ignore some attributes' diffrence
void XMLDOMNodeParser::InitialAttributesExcludedList()
{
if (attributesExcludedList.empty())
{
attributesExcludedList.insert(L"CreationTime");
attributesExcludedList.insert(L"ProductLevel");
attributesExcludedList.insert(L"Creator");
}
}
/////////////////////////////////////////////////////////////////////////
// If a presentation's type is build-in, ignore the presentation's diffrence
void XMLDOMNodeParser::InitialPresentationList()
{
if (presentationBuildinList.empty())
{
presentationBuildinList.insert(L"00006596-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006590-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006591-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006593-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006594-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"00006595-0000-0000-0000-000000000000");
presentationBuildinList.insert(L"000059D9-0000-0000-0000-000000000000");
}
}
判断两个XML文件结构与内容是否相同的更多相关文章
- C# 通过比对哈希码判断两个文件内容是否相同
1.使用System.security.Cryptography.HashAlgorithm类为每个文件生成一个哈希码,然后比较两个哈希码是否一致. 2. 在比较文件内容的时候可以采用好几种方法.例如 ...
- JavaScript判断两个对象内容是否相等
ES6中有一个方法判断两个对象是否相等,这个方法判断是两个对象引用地址是否一致 let obj1= { a: 1 } let obj2 = { a: 1 } console.log(Object.is ...
- java中判断两个字符串是否相等的问题
我最近刚学java,今天编程的时候就遇到一个棘手的问题,就是关于判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写.在java中,用的是eq ...
- Java 判断两个对象是否相等
一.使用 == 与 equals == : 它的作用是判断两个对象的地址是不是相等.即,判断两个对象是不是同一个对象.(基本数据类型==比较的是值,引用数据类型==比较的是内存地址) equals() ...
- SWF运行时判断两个DisplayObject是否同个类型,属于flash professional库中的同一个元件
一般我们判断两个实例对象是否同样的类型,可以用typeof得到对象类型,然后用==号比较. typeof适用于原生类型. 而对于自定义类型,虽然typeof得到的都是Object,但还有更强的招数:g ...
- c#如何判断两个对象是否相等
在c#中判断对象相等,这是对引用类型进行判断,而不是对值类型,如果是对字符串,或者是数值进行判断相等只需要用==运算符就可以了. 对两个对象用==运算符,只能判断他们两个在内存中的地址是否一样的. ...
- 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)
需求: 编写一个diff工具,用于判断两个目录下所有的改动 详细介绍: 有A和B两个目录,目录所在位置及层级均不确定 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加.修改.删除),将有改 ...
- XML文件结构和基本语法
XML文件的结构性内容,包括节点关系以及属性内容等等.元素是组成XML的最基本的单位,它由开始标记,属性和结束标记组成.就是一个元素的例子,每个元素必须有一个元素名,元素可以若干个属性以及属性值. x ...
- 9-2、大型项目的接口自动化实践记录----递归判断两个json串是否相等
1.已知json串构成的情况下判断 先构造一下场景,假设已经把各个数据都移除掉不对比的字段 图1 预期.实际结果,复杂接口返回多层嵌套json时,同下 图2 预期.实际结果值为:{child_json ...
随机推荐
- [虚拟化/云] kvm的架构分析
预备知识 1. 客户机物理页框到宿主机虚拟地址转换 http://blog.csdn.net/zhuriyuxiao/article/details/8968781 http://www.tuicoo ...
- python访问cloudstack的api接口
1.CloudStack API 如同 AWS API 一样,CloudStack API 也是基于 Web Service,可以使用任何一种支持 HTTP 调用的语言(例如 Java,python, ...
- 全互联结构DVPN综合配置示例
以下内容摘自正在全面热销的最新网络设备图书“豪华四件套”之一<H3C路由器配置与管理完全手册>(第二版)(其余三本分别是:<Cisco交换机配置与管理完全手册>(第二版).&l ...
- 切点算法模板(Cut-vertex)
下面是一个模板被切割点,也cut_vertex_num[]排列(array)什么是切 - 点记录 Int cut_vertex_num[]; void dfs(int cur,int pa) { in ...
- 获取第上一个兄弟元素 屏蔽浏览器的差异(PreviousElementSibling)
//获取element上一个兄弟元素 function getPreviousElementSibling(element){ //能力检测 判断是否支持PreviousElementSibling ...
- Excel几个常用操作
1.冻结第一行:视图->冻结窗格->冻结首行 2.第一行自动筛选:选择要设置的行,数据->筛选->自动筛选 3.在单元格中添加下拉菜单:(1)选中一列,数据->数据验证. ...
- Memcached基础
1.实例化 MemcachedClient client = new XMemcachedClient(); public XMemcachedClient() public XMemcachedCl ...
- iOS 导航条的影响
如果是push出来的控制器,self.view的(0,0)点从状态栏下面开始: 如果有present出来的控制器,self.view的(0,0)点包含状态栏:
- 提供一段Excel获取Title的标题,类似于A、AA、AAA,我们操作Excel的时候通常根据次标题来获取一定的操作范围。
/******************************************** FormatExcelColumTitle Purpose Get excel title like &qu ...
- USACO Section 4.3 Street Race(图的连通性+枚举)
虽说是IOI'95,但是也是挺水的..for 第一问,n最大为50,所以可以直接枚举起点和终点之外的所有点,然后dfs判断是否连通:for 第二问,易知答案一定是第一问的子集,所以从第一问中的答案中枚 ...