XMLCC: XML생성을 위한 C++ 라이브러리

어쩌다 이름이 XMLCC가 되었는지는 모릅니다. 그냥 손가락에서 저렇게 나왔어요. 신의 뜻이라 생각하고 넘어갑니다. 씨익-

사건의 발단은 회사에서 XML을 생성해서 서버에 전송해야 하는 일이 생겨서 std::stringstream을 이용해 XML을 생성하다 보니 코드가 너무 지저분해지는 겁니다. 후.. 이런저런 고민을 한 끝에 좀 깔끔하게 짤 수 있는 프로그램을 작성해보기로 했지요. 🙂

일단 만들 XML부터 봅시다.

1<author>Crow, Lim</author>

저런 문서를 하나 만들기 위해, std::stringstream을 사용하게 되면 다음과 유사한 코드가 등장할겁니다.

1std::stringstream ss;
2 
3void make_xml(std::stringstream&amp; ss, std::string const&amp; title, std::string const&amp; author, ...(생략)...)
4{
5ss << "<author>";
6ss << author;
7ss << "</author>";
8}

보기만 해도 끔찍하군요. 엄청난 escaping문자들.. 보기만해도 어지럽습니다. 만약 다음과 같이 작성할수 있다면 행복할텐데요.

01void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
02{
03// 들여쓰기는 보기 편하라고 한 것 입니다.
04element_start(ss, "book");
05    attribute(ss, "title", title);
06 
07element_start(ss, "author");
08    text(ss, author);
09element_end(ss, "author");
10 
11element_start(ss, "date");
12    attribute(ss, "year", year);
13    attribute(ss, "month", month);
14    attribute(ss, "day", day);
15element_end(ss, "date");
16 
17element_end(ss, "book");
18}

아쉽게도 위와 같은 코드는 작동하지 못합니다. XML의 특성상 attribute는 element 시작태그 내에서 정의가 되어야 하기 때문이지요. 물론, 이를 해결하기 위해 element_start의 인수로 attribute의 list를 넘겨주는 방법도 있습니다만, 이 방법은 해당하는 리스트를 생성해야한다는 단점이 있습니다. 의미상 코드에서 정적으로 정의가 가능한 XML을 굳이 리스트를 생성하면서 만들 필요는 없지요.

이를 위해 element_start()의 리턴을 객체로 주는 방법을 사용했습니다. 🙂 그 객체에서 시작태그를 생성하고 attribute도 처리하는 거지요.

01void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
02{
03    element_start(ss, "book")
04        .attribute("title", title);
05 
06    element_start(ss, "author");
07    text(ss, author);
08    element_end(ss, "author");
09 
10    element_start(ss, "date")
11        .attribute("year", year)
12        .attribute("month", month)
13        .attribute("day", day);
14    element_end(ss, "date");
15 
16    element_end(ss, "book");
17}

element_start()의 리턴객체에서 attribute를 호출합니다. 자세히 보시면 뒤에 세미콜론(;)이 없는 라인들이 있는데 사실 한 expression이란 겁니다. 일종의 호출 체인이랄까요?

xmlcc.h에 있는 주요 부분을 보면 다음과 같습니다.

01struct element_start_tag
02{
03    std::ostream& out_;
04 
05    element_start_tag(std::ostream& out, std::string const& name)
06        : out_(out)
07    {
08        out_ << "<" << name;
09    };
10 
11    element_start_tag(element_start_tag const& tag)
12        : out_(tag.out_)
13    {
14    };
15 
16    element_start_tag& attribute(bool use, std::string const& name, std::string const& value)
17    {
18        if(use == true)
19        {
20            return attribute(name, value);
21        }
22 
23        return *this;
24    }
25 
26    element_start_tag& attribute(std::string const& name, std::string const& value)
27    {
28        out_ << " ";
29        text(out_, name);
30        out_ << "=\"";
31        text(out_, value);
32        out_ << "\"";
33        return *this;
34    }
35 
36    template<class ValueT, class FilterFuncT>
37        element_start_tag& attribute(std::string const& name, ValueT const& value, FilterFuncT const& f)
38    {
39        out_ << " ";
40        text(out_, name);
41        out_ << "=\"";
42        text(out_, f(value));
43        out_ << "\"";
44 
45        return *this;
46    }
47 
48    template<class Func>
49        element_start_tag& attribute_with_generator(std::string const& name, Func const& f)
50    {
51        out_ << " " << name << "=" << "\"";
52        f(out_);
53        out_ << "\"";
54        return *this;
55    }
56 
57    element_start_tag& operator()(std::string const& name, std::string const& value)
58    {
59        return attribute(name, value);
60    }
61 
62    element_start_tag& operator()(bool use, std::string const& name, std::string const& value)
63    {
64        return attribute(use, name, value);
65    }
66 
67    void close_now()
68    {
69        out_ << "/";
70    }
71 
72    ~element_start_tag()
73    {
74        out_ << ">";
75    };
76};
77 
78 
79inline element_start_tag element_start(std::ostream& out, std::string const& name)
80{
81    return element_start_tag(out, name);
82}

보시면, 생성자에서는 시작태그의 요소이름까지만 출력을 하고, 소멸자에서 태그를 닫습니다. attribute를 사용하면 stream에 attribute를 출력해주지요. 또한, close_now()를 호출하면 “/”를 출력시켜줌으로써 스스로 닫는 element를 생성해줍니다. 그리고, element_start()는 이 element_start_tag를 생성해주는 도우미 역할을 하지요.

attribute메소드는 operator()를 이용해도 사용할 수 있습니다. 이 경우 코드가 훨씬 간단해지지요.

01void make_xml(std::stringstream& ss, std::string const& title, std::string const& author, ...(생략)...)
02{
03element_start(ss, "book")
04    ("title", title);
05 
06    element_start(ss, "author");
07        text(ss, author);
08    element_end(ss, "author");
09 
10    element_start(ss, "date")
11        ("year", year)
12        ("month", month)
13        ("day", day);
14    element_end(ss, "date");
15 
16element_end(ss, "book");
17}

사실 XML생성에 있어서 다른 XML의 구성요소들은 크게 문제가 되지 않는데, 가장 머리 아픈 부분은 element의 시작태그와 그 안에 내포된 attribute인지라 이런 잔머리를 굴려봤습니다. 🙂 덕분에 소스코드는 escape문자 없이 깔끔해졌군요. 🙂 잇힝-

좀 더 삽질해서 DSL(Domain Specific Langauge)형태로 만들어 보려고 했는데, 시간도 없고 이정도면 쓸만한거 같아서 여기서 멈췄습니다. 🙂

xmlcc.h 다운로드

글쓴이: crowmania

Chief Developer in Somansa Guitar/Vocal in Tonics Member of Justice Party

“XMLCC: XML생성을 위한 C++ 라이브러리”의 2개의 생각

  1. 안녕하세요. 구글링중에 XML관련 좋은 내용을 보아 들립니다.
    ” xmlcc.h 다운로드 ” 를 클릭하여 header를 받고 싶었습니다만.
    엉뚱한 링크로 가네요. 혹시 메일로 받을 수 있을 수 있을까요?
    그럼 행복한 하루 되십시오~

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다