2011. 1. 21. 10:31

C++ XML Parser 공유 (RapidXML, UTF8 to WCHAR)


(src_r02.zip이 최신 버전입니다.)

RapidXml 라이브러리를 이용한 C++ XML Parser를 공유합니다.
RapidXml은 알려진 XML Parser 라이브러리로,

  • Boost Software License
  • MIT License

를 따릅니다. MIT License가 있으니, 아무런 제약없이 사용가능할 겁니다.

되도록 간단하고 직관적으로 C++ wrapping 하였습니다.
그리고, XML 파일에 UTF BOM이 있는 경우도 처리하도록 하였으며,
XML 파일에 UTF-16과 같은 BOM이 있는 경우는 지원하지 않습니다.

내부 버퍼는 UTF8(ANSI)로 저장하며,
이름이나 값을 리턴할 떄는 WCHAR(UNICODE)로 리턴하니 사용상에 불편함은 없을 겁니다.

다음은 사용예입니다.
대략적인 예로서, 리턴값 체크는 해주어야 합니다.

#include "RapidXmlHelper.h"
...
	CRaipidXmlHelper::HANDLE_NODE	hNode = NULL;
	CRaipidXmlHelper::HANDLE_ATTR	hAttr = NULL;
	LPCTSTR							lpszValue = NULL;
	CRaipidXmlHelper				cXml;

	// context 생성
	cXml.AllocXmlFromFile(TEXT("C:\Test.xml"));

	// root를 구한다.
	hNode = cXml.GetRoot();
	lpszValue = cXml.GetName(hNode);

	// 처음으로 옮긴다.
	hNode = cXml.FirstNode(hNode);
	lpszValue = cXml.GetName(hNode);

	// 해당 Node의 Attribute를 구하고,
	// 이름과 값을 구하는 과정이다.
	{
		hAttr = cXml.FirstAttr(hNode);
		lpszValue = cXml.GetName(hAttr);
		lpszValue = cXml.GetValue(hAttr);

		// 끝까지 가면 hAttr에 NULL이 리턴된다.
		hAttr = cXml.NextAttr(hAttr);
	}

	// 다음 Node로 옮기는 과정이다.
	hNode = cXml.NextSibling(hNode);
	lpszValue = cXml.GetName(hNode);

	// 끝까지 가면 hNode에 NULL이 리턴된다.

	// 다음 Nest로 들어가본다.
	hNode = cXml.FirstNode(hNode);
	lpszValue = cXml.GetName(hNode);

	// Traversing
	for (;;)
	{
		if (NULL == hNode) break;
		hNode = cXml.NextSibling(hNode);
		...
	}


CRapidXmlHelper의 API는 다음과 같습니다.

DWORD AllocXmlFromFile(IN LPCTSTR lpszFilePath)
DWORD AllocXmlFromMemoryA(IN LPCTSTR lpszXmlA)
DWORD AllocXmlFromMemoryW(IN LPCWSTR lpszXmlW)

[return] Win32 Error Code
[Argument]

  1. lpszFilePath
    ; XML 파일 경로. UTF8 형태의 포맷이 지원된다.
  2. lpszXmlA
    ; ANSI(UTF8)를 받는다.
  3. lpszXmlW
    ; Unicode를 받는다.

각 함수의 특징에 따라 XML 메모리를 할당한다.

VOID DeAlloc();

Alloc* 함수로 할당된 메모리를 해제한다.
본 Class가 파괴될 때도 한번 호출 된다.

HANDLE_NODE GetRoot(VOID)

XML의 Root Node 객체를 리턴한다.

HANDLE_NODE FirstNode(IN HANDLE_NODE hNode)
HANDLE_NODE NextSibling(IN HANDLE_NODE hNode)

지정된 XML Node 객체 하부(Nest)를 Traverse 하는데 사용된다.
즉, FirstNode를 통해 하부 Node를 구하고, NextSibling을 통해 해당 하부 Node의 이웃 Node를 리턴한다.
만일, 하부 Node가 없다면, FirstNode(...)는 NULL을 리턴하고,
더이상의 이웃 Node가 없다면, NextSibling(...)은 NULL을 리턴한다.

HANDLE_ATTR FirstAttr(IN HANDLE_NODE hNode)
HANDLE_ATTR NextAttr(IN HANDLE_ATTR hAttr)

지정된 Node의 XML Attribute를 Traverse 하는데 사용된다.
즉, FirstAttr을 통해 지정된 Node의 첫 Attribute 객체를 구하고, NextAttr을 통해 다음 Attribute를 리턴한다.
만일, Node의 Attribute가 없다면, FirstAttr(...)은 NULL을 리턴하고,
진행중 더이상의 다음 Attribute가 없다면, NextAttr(...)은 NULL을 리턴한다.

LPCWSTR GetName(IN HANDLE hHandle)
LPCWSTR GetValue(IN HANDLE hHandle)

GetName을 통해 Node 혹은 Attribute의 이름을 UNICODE로 구합니다. 값이 없거나 잘못된 상태라면 NULL을 리턴합니다.
GetValue를 통해 Node 혹은 Attribute의 값을 UNICODE로 리턴합니다. 값이 없거나 잘못된 상태라면 NULL을 리턴합니다.

* revision history
- src_r01.zip : <? ... encoding="utf-8">과 같이 utf-8이 아닌경우,
  시스템의 code-page(CP_ACP)를 기준으로 UTF16으로 변환하고 UTF8로 다시 변환하여 사용한다.
  xml의 data field에 cdata가 있는 경우 GetValue로 가져올 수 있도록 수정
- src_r02.zip : CString dependency 제거