经过几天的研究,终于完成了XML的SAX2 Schema校验。
关于什么是Schema校验,在这里就不废话了,可以去google一下。
下面我就把整个的过程写一下。
借用网上的一些XML文件。
store.xsd文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- edited with XMLSpy v2011 (http://www.altova.com) by kevin (neuseeker) -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="store">
<xs:annotation>
<xs:documentation>Comment describing your root element</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element ref="goods" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="goods">
<xs:complexType>
<xs:sequence>
<xs:element ref="id"/>
<xs:element ref="price"/>
<xs:element ref="vendor"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="id" type="xs:string"/>
<xs:element name="price" type="xs:double"/>
<xs:element name="vendor" type="xs:string"/>
</xs:schema>
test_good.xml
<?xml version="1.0" encoding="utf-8"?>
<store>
<goods>
<id>"0001"</id>
<price>3.50</price>
<vendor>"xxx奶粉"</vendor>
</goods>
<goods>
<id>"0002"</id>
<price>66.00</price>
<vendor>"yyy进口奶粉"</vendor>
</goods>
</store>
test_bad.xml
<?xml version="1.0" encoding="utf-8"?>
<store>
<goods>
<id>"0001"</id>
<price>3.50</price>
<vendor>"xxx奶粉"</vendor>
<name>"四路奶粉"</name>
</goods>
<goods>
<id>"0002"</id>
<price>"66.00"</price>
<vendor>"yyy进口奶粉"</vendor>
</goods>
</store>
以以上三个文件为例,进行验证。
代码如下:
OperContentHandler .hpp
#pragma once
#if !defined(AFX_A1CONTENTHANDLER_H__E0CFBC18_CCC1_42F3_B0A4_B03331AB9693__INCLUDED_)
#define AFX_A1CONTENTHANDLER_H__E0CFBC18_CCC1_42F3_B0A4_B03331AB9693__INCLUDED_
#include "stdafx.h"
#include <xercesc\sax2\DefaultHandler.hpp>
#include <iostream>
using namespace std;
class OperContentHandler :
public DefaultHandler
{
public:
OperContentHandler();
virtual ~OperContentHandler();
void characters
(
const XMLCh* const chars,
const XMLSize_t length
);
void endElement
(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
);
virtual void startElement
(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
, const Attributes& attrs
);
// 继承自default Handle, 用于对错误的处理
void error(const SAXParseException& exc)
{
throw exc;
}
void fatalError(const SAXParseException& exc)
{
throw exc;
}
void warning(const SAXParseException& exc)
{
throw exc;
}
wstring goods;
};
#endif // !defined(AFX_A1CONTENTHANDLER_H__E0CFBC18_CCC1_42F3_B0A4_B03331AB9693__INCLUDED_)
OperContentHandler.cpp
// OperContentHandler.cpp: derived class from SAXContentHandlerImpl
#include "stdafx.h"
#include "OperContentHandler.hpp"
//
// Construction/Destruction
//
OperContentHandler::OperContentHandler()
{
}
OperContentHandler::~OperContentHandler()
{
//object destruction is handled by the Release() impl of parent class
}
void OperContentHandler::startElement
(
const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname
, const Attributes& attrs
)
{
wstring sNodeName = localname;
goods += sNodeName;
}
void OperContentHandler::endElement( const XMLCh* const uri,
const XMLCh* const localname,
const XMLCh* const qname )
{
wstring sNodeName = localname;
goods += sNodeName;
}
void OperContentHandler::characters( const XMLCh* const chars,
const XMLSize_t length )
{
wstring sNodeValue(chars);
goods += sNodeValue;
}
SAXSchema.cpp文件
// SAXSchema.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "SAX2XMLReader.hpp"
#include "xercesc\sax2\XMLReaderFactory.hpp"
#include "OperContentHandler.hpp"
#include <stdio.h>
#include <string>
using namespace std;
using namespace xercesc_3_1;
void memcpyEx(wchar_t* wDest, int iDsize, wstring src)
{
size_t iCpy = src.length() + 1; // '\0'
size_t iMax = iDsize/sizeof(wchar_t);
if (src.empty())
{
memset(wDest, 0, iMax);
return;
}
if (iCpy < iMax)
{
wmemset(wDest, 0, iMax);
wmemcpy(wDest, src.c_str(), iCpy);
}
else
{
wmemset(wDest, '\0', iMax);
iCpy = iMax - 1;
wmemcpy(wDest, src.c_str(), iCpy);
}
}
int _tmain(int argc, char* argv[])
{
wchar_t *xmlPath = L"H:\\WorkSpace\\MarkupTest\\MarkupTest\\test_bad.xml";
wchar_t *sxsdPath = L"H:\\WorkSpace\\MarkupTest\\MarkupTest\\store.xsd";
OperContentHandler handler;
wstring xsdPath(sxsdPath);
try{
XMLPlatformUtils::Initialize();
}
catch (...) {
return 1;
}
SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
parser->setFeature(XMLUni::fgXercesSchema, true);
parser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, true);
parser->setFeature(XMLUni::fgXercesContinueAfterFatalError, false);
// 当Schema存在命名空间时,使用此部分;其中L"http://www.iec.ch/61850/2003/SCL "为我在试验时的一个命名空间
//wstring xsd = L"http://www.iec.ch/61850/2003/SCL " + xsdPath;
//wchar_t xsdCh[400];
//memcpyEx(xsdCh, sizeof(xsdCh), xsd);
//parser->setProperty(XMLUni::fgXercesSchemaExternalSchemaLocation, xsdCh);
// 当Schema不存在命名空间时,使用这个方法;在上述的XML中应该使用这个方法来设置模板文件
parser->setProperty(XMLUni::fgXercesSchemaExternalNoNameSpaceSchemaLocation, sxsdPath);
parser->setContentHandler(&handler);
parser->setErrorHandler(&handler);
try
{
parser->parse(xmlPath);
}
catch (const SAXParseException& toCatch)
{
int iCol = toCatch.getColumnNumber();
int iRow = toCatch.getLineNumber();
wchar_t cCol[10], cRow[10];
_itow(iCol, cCol, 10);
_itow(iRow, cRow, 10);
wstring sCol(cCol);
wstring sRow(cRow);
wstring sMes(toCatch.getMessage());
// 此处将展示错误信息
wstring wValue = L"行:" + sRow + L",列:" + sCol+ L" " + sMes;
return 0;
}
catch (const OutOfMemoryException &ome)
{
wstring sMes(ome.getMessage());
return false;
}
catch (const XMLException &xe)
{
wstring sMes(xe.getMessage());
return false;
}
delete parser;
XMLPlatformUtils::Terminate();
return 0;
}
当进行校验完成后,在wValue的值为:行:7,列:9 no declaration found for element 'name'
上面就是整个Schema校验的校验过程,注意点主要为一下:
一,
在上述的OperContentHandler中,我们重写了继承自DefaultHandler的三个错误处理方法。如果不重写这些方法,OperContentHandler则只是处理了解析过程中和文档内容相关的事件。如果不注册一个错误处理器来处理的话,那么错误事件将不会被报告,而且解析器会出现不可预知的行为。在解析过程中产生的错误被分成了3类,它们分别是warning,error,以及fatalerror,也就是说在ErrorHandler中有这么三个相应的方法来处理这些错误事件。
二,
通过设置fgXercesSchemaExternalNoNameSpaceSchemaLocation或者fgXercesSchemaExternalSchemaLocation属性来设置XSD文件;之前一直以为可以通过loadGrammar()来进行XSD的设置,但是总是不行,可能是理解不到位,不会使用。所以改成了设置属性这种方法。
参考文献:
1,http://www.cnblogs.com/kevin_neu/archive/2013/04/16/3003514.html
2,http://www.codeproject.com/Articles/11085/Parsing-XML-using-a-C-wrapper-for-SAX2
3,http://www.ibm.com/developerworks/cn/xml/x-xsdxerc.html
4,http://xerces.apache.org/xerces-c/program-sax2-3.html
5,https://www.ibm.com/developerworks/cn/xml/x-saxhandle/