判断两个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 ...
随机推荐
- Sql优化(三) 关于oracle的并发
Oracle的并发技术可以将一个大任务分解为多个小任务由多个进程共同完成.合理地使用并发可以充分利用系统资源,提高效率.一. 并发的种类Parallel queryParallel DML(PDML) ...
- 传智播客C/C++学院年薪24-50万招聘C/C++讲师
C/C++技术讲师 6名 (北京,年薪:24-50万) 传智播客C/C++课程培训体系如下: 1.C语言,世界五百强C语言面试训练 2.C++语言,世界五百强C++语言面试训练 3.数据结构与算法,世 ...
- Google Play Services Library update and missing symbol @integer/google_play_services_version
转自http://stackoverflow.com/questions/19843784/google-play-services-library-update-and-missing-symbol ...
- objective-c 中代码块(blocks)
在ios4之后,引入了代码块的特性,在gcd中会经常的用到,所以决定好好的看看代码块文档,把这块总结一下.从头开始讲解代码块. 1.声明和使用代码块 一般用^操作符声明一个块变量,并作为块的开始符.而 ...
- <原>ASP.NET 学习笔记之应养成的良好习惯
写ASP.NET时应有的良好习惯(不定时增加): 1.view的名称一定要与对应的actionMethod的名称相同:从原理上看,客户端通过url(一般形式为http://xxx/controller ...
- 锁·——lock关键字详解
作 者:刘铁猛 日 期:2005-12-25 关键字:lock 多线程 同步 小序 锁者,lock关键字也.市面上的书虽然多,但仔细介绍这个keyword的书太少了.MSDN里有,但所给的代码非常 ...
- SQL中存储过程和自定义函数的区别(转载)
存储过程: 存储过程可以使得对数据库的管理.以及显示关于数据库及其用户信息的工作容易得多.存储过程是 SQL 语句和可选控制流语句的预编译集合,以一个名称存储并作为一个单元处理.存储过程存储在 ...
- ThinkPHP第二十二天(表单令牌、相对路径、扩展配置载入、$Think获取系统变量、$_SERVER('HTTP_REFERER')前页地址)
1.表单令牌开启配置 'TOKEN_ON'=>true 2.相对路径:在thinkphp中,存在单入口index.php,所以程序中的根目录都是以index.php所在的文件夹为根目录,故用./ ...
- python初探-数据类型
数据类型 可以使用BIF type()来查看对象的类型 数字 int float long 布尔(bool) True 机内表示1,机器识别非0 False 机内表示0,机器识别0 空值 None 字 ...
- Minix
[1] MINIX是一种基于微内核架构的类UNIX计算机操作系统,由Andrew S. Tanenbaum发明.MINIX最初发布于1987年,开放全部源代码给大学教学和研究工作.2000年重新改为 ...