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 ...
随机推荐
-
编写更好的jQuery代码的建议
讨论jQuery和javascript性能的文章并不罕见.然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码.好的代码会带来速度的提升.快速渲染 ...
-
Codeforces Round #248 (Div. 2) B. Kuriyama Mirai&#39;s Stones
题目简单描述就是求数组中[l,r]区间的和 #include <iostream> #include <vector> #include <string> #inc ...
-
golang的验证码相关的库
识别库 https://github.com/goghcrow/capture_easy 生成验证码的库 https://github.com/hanguofeng/gocaptcha 生成图片水印 ...
-
mysql 一个较特殊的问题:You can&#39;t specify target table for update in FROM clause
mysql 一个较特殊的问题:You can't specify target table for update in FROM clause 即:不能先select出同一表中的某些值,再update ...
-
ObjectOutputStream 追加写入读取错误
摘自http://blog.csdn.net/mitkey/article/details/50274543 问题描述: 用类ObjectOutputStream向文件写读对象时,碰到一个问题:新建一 ...
-
Directx11学习笔记【二十】 使用DirectX Tool Kit加载mesh
本文由zhangbaochong原创,转载请注明出处:http://www.cnblogs.com/zhangbaochong/p/5788482.html 现在directx已经不再支持.x文件了, ...
-
每天五分钟-javascript数据类型
javascript数据类型分为基本数据类型与复杂数据类型 基本数据类型包括:string,number,boolean,null,undefined,symbol(es6) 复杂数据类型包括:obj ...
-
【git】一台机器上使用不同的git账号
1.生成一个新的自定义名称的公钥: ssh-keygen -t rsa -C "shangxiaofei3@163.com" -f ~/.ssh/sxfself 一直点击回车 执行 ...
-
利用MVC Chart 打造后台图表、前端图表
应用场景是这个样子的:要做导出数据到PDF的功能,涉及到文本.表格.图表等内容.在做图表功能时,发现之前用Highcharts做的图表根本就不能集成到PDF中.这里需要一个能在程序后台就生成图表的功能 ...
-
MonGoDB问题笔记
1. 最近出了一个问题,就是按日期查查不出来数据,经过折腾后发现,mongodb中的存储数据的日期格式与查询时使用的日期格式不匹配,导致查询出不来 //{"$and":[{ &qu ...