Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / XML
Article

Object-oriented XML Parser

Rate me:
Please Sign up or sign in to vote.
4.36/5 (18 votes)
28 Aug 20013 min read 223K   3.1K   58   44
Object-oriented parser to read/write XML files using MSXML parser

Introduction

I had a task to write an XML parser to read/write XML files with list of possibilities (requirements).

Requirements:

  • Comfort for developer (user of parser)
  • Possibility of recording and reading using the same instance of parser
  • No use of MFC
  • User object writes and reads itself
  • Possibility to write in binary stream, without changing source of user objects
  • Possibility of creation of proxy classes for reading/writing of user classes, modification of which is forbidden
  • Same code in user object for reading and writing
  • Possibility of reading/writing simple types
  • Possibility of reading/writing objects (with possibility of determination of type during loading)
  • Possibility of reading/writing vector, map of simple and complex objects
  • Possibility of reading/writing the attributes
  • Absences of restrictions on nesting of one type in others
  • Minimization of operations "if"
  • Unimportance of order of tags in parent tag
  • Absence of support namespace and schema

Solution

Based on several ideas:

  1. Using of parser-a based on sample from Windows Platform SDK
  2. Each user object that understand that it is his time to be read, throws itself just like "throw this"
  3. Than parser catch him and put into stack and all calls will be send to this object, until anyone else throw itself or end of object will be found
  4. Each user object have only one function "determine(ProxyObject& proxy)" it should ovewrite
  5. Proxy - it is an abstract object that have some virtual functions to read/write some types of data and proxy implementations which are made automaticaly by parser to exactly "know" the type of stream and current operation
  6. There is a template class with template constructor, that gets members of user objects, proxy object, "name" of the object to read
  7. This template class understands itself what to do with that members and call corresponding proxy's functions.
  8. There are some proxy classes that can read/write string, int and float.

Here is that wonderful template:

template <class T = void*>
class determineMember
{
    public:
    template <class OT>
    determineMember(OT& t, 
        const char* str,XmlParserProxy& p,T* _t=0) throw(XmlObject*)
    {
        determine(t,str,p,_t,type(t));
    }
};

Currently my parser satisfies all requirements above. And this is example of user objects:

struct Nodes : XmlObject
{
    vectorDel<Node*>  m_nodes;
    virtual void determine(XmlParserProxy& p) throw(XmlObject*)
    {
        determineMember<StartNode>(m_nodes,"start-node",p);
        determineMember<InteractionNode>(m_nodes,"interaction-node",p);
    }
};
struct Pipeline : XmlObject
{
  string m_name;
  int    m_count;
  float  m_sum;
  SomeObject* m_someObject;
  vector<string> m_transitions;
  vector<int> m_point;
  Nodes m_nodes;
  map<int,NodeDisplay> m_map;
  virtual void determine(XmlParserProxy& p) throw(XmlObject*)
  {
    determineMember<>(m_name,"name",p);
    determineMember<>(m_count,"count",p);
    determineMember<>(m_sum,"sum",p);
    determineMember<FirstObject>(m_someObject,"FirstObject",p);
    determineMember<SecondObject>(m_someObject,"SecondObject",p);
    determineMember<Nodes>(m_nodes,"nodes",p);
    determineMember<string>(m_transitions,"transition",p);
    determineMember<int>(m_point,"point",p);
    determineMember<>(m_map,"Map",p);
  }
};

Note that you can have pointers and vectors of objects and have different classes there.

This is an example of using the parser:

CoInitialize(NULL);

Document doc;
XML::ZXmlParserImpl parser(&doc);

parser.parse(_bstr_t("test.xml"));

parser.save(_bstr_t("saved_test.xml"));

CoUninitialize();

Look at this sample XML file:

<pipeline>
   <!-- string -->
        <name>Default</name>
        <!-- int -->
         <count>2</count>
        <!-- float -->
         <sum>1.234567</sum>
        <!-- object having type from set of types-->
        <SecondObject>
          <second>1</second>
        </SecondObject>
         <!-- uncomment FirstObject and comment SecondObject 
                     to make FirstObject instead of SecondObject
        <FirstObject>
          <first>1</first>
        </FirstObject>
        -->
        <!-- new XMLObject of type Nodes-->
        <nodes>
                <!-- insert in vector<Node> new StartNode, 
                                               string attribute-->
                <start-node id="Start">
                        <!-- insert vector<NodeDisplay> new NodeDisplay-->
                        <node-display>
                <x-center>0</x-center>
                <y-center>0</y-center>
            </node-display>
            <node-display>
                                <x-center>1</x-center>
                                <y-center>1</y-center>
            </node-display>
        </start-node>
                <!-- insert in vector<Node> 
                        new InteractionNode, string attribute-->
                <interaction-node id="It">

                    <!-- two string attributes-->
                   <template dynamic="false" index="2">
                           testS
                   </template>
                </interaction-node>
    </nodes>

<!-- vector of strings -->
        <transition>Tr1</transition>
        <transition>Tr2</transition>

<!-- vector of ints -->
        <point>3</point>
        <point>2</point>
        <point>1</point>

<!-- and now - std::map<int,NodeDisplay> -->
        <Map>
          <pair>
            <name>1</name>
            <value>
                <x-center>1</x-center>
                <y-center>2</y-center>
            </value>
          </pair>
          <pair>
            <name>2</name>
            <value>
                <x-center>4</x-center>
                <y-center>3</y-center>
            </value>
          </pair>
        </Map>

</pipeline>

This table shows how to implement determine function with different members

Member variableSource in determineComments
string m_name;determineMember<>(m_name,"name",p); 
string m_name;determineMember<AtribValue>(m_name,"name",p);Use AtribValue to indicate that name is attribute but not a node
int m_count;determineMember<>(m_name,"count",p); 
float m_sum;determineMember<>(m_name,"sum",p); 
SomeObject* m_someObject;determineMember <FirstObject>(m_someObject, "FirstObject",p);If tag FirstObject found, then new FirstObject will be assigned to m_someObject
SomeObject* m_someObject;determineMember <SecondObject>(m_someObject, "SecondObject",p);If tag SecondObject found, then new SecondObjectwill be assigned to m_someObject
SomeObject m_nodes;determineMember<>(m_nodes,"nodes",p); 
std::vector<int> m_point;determineMember<int>(m_point,"point",p); 
std::vector<string> m_transitions;determineMember <string>(m_transitions, "transition",p); 
std::map<int,SomeObject> m_map;determineMember<>(m_map,"Map",p); 
std::vector<Node*> m_nodesdetermineMember<StartNode>(m_nodes,"start-node",p);If tag start-node found, then new StartNode will be made and inserted in m_nodes
std::vector<Node*> m_nodes;determineMember <InteractionNode>(m_nodes, "interaction-node",p);If tag interaction-node found, then new InteractionNode will be made and inserted in m_nodes

In demo project I have shown how to implement classes for reading/writing std::map not in parser's source files.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Russian Federation Russian Federation
Mike has been programming in C/C++ for 11 years and Visual C++/MFC for 4 years. His background includes pure and applied mathematics, engineering and physics, and he is currently based in Moscow, Russia.

Comments and Discussions

 
GeneralCompile errors Pin
Member 6681898-May-08 13:14
Member 6681898-May-08 13:14 
QuestionHo do i add MSXMl6.dll to my installer ? Pin
code4jigar5-Sep-06 2:18
code4jigar5-Sep-06 2:18 
AnswerRe: Ho do i add MSXMl6.dll to my installer ? Pin
Mike Melnikov29-Sep-06 14:01
Mike Melnikov29-Sep-06 14:01 
GeneralWriting to an XML file Pin
wcovuaku28-Oct-04 5:56
wcovuaku28-Oct-04 5:56 
GeneralRe: Writing to an XML file Pin
Mike Melnikov28-Oct-04 19:07
Mike Melnikov28-Oct-04 19:07 
QuestionHow to serialize custom map&lt;CString,Object** &gt;? Pin
vgrigor13-Oct-04 22:23
vgrigor13-Oct-04 22:23 
Generalsome problems: map - error Pin
vgrigor13-Oct-04 20:41
vgrigor13-Oct-04 20:41 
GeneralHelp please Pin
Anonymous25-Aug-04 22:38
Anonymous25-Aug-04 22:38 
GeneralRe: Help please Pin
Colin Angus Mackay25-Aug-04 23:43
Colin Angus Mackay25-Aug-04 23:43 
GeneralVisual Studio .NET 2003 Pin
Sevig11-Jul-04 20:22
Sevig11-Jul-04 20:22 
GeneralRe: Visual Studio .NET 2003 Pin
Mike Melnikov25-Aug-04 22:55
Mike Melnikov25-Aug-04 22:55 
Generallitle trouble with charsets Pin
Member 39149015-Jan-04 21:47
Member 39149015-Jan-04 21:47 
GeneralRe: litle trouble with charsets Pin
Mike Melnikov15-Jan-04 22:56
Mike Melnikov15-Jan-04 22:56 
GeneralRe: litle trouble with charsets Pin
aGodInHost1-May-04 5:30
aGodInHost1-May-04 5:30 
GeneralProblem with MFC release.. Pin
EPulse21-Dec-03 17:45
EPulse21-Dec-03 17:45 
GeneralRe: Problem with MFC release.. Pin
EPulse21-Dec-03 20:52
EPulse21-Dec-03 20:52 
GeneralLimitation Pin
Sahbi15-Jun-03 0:45
Sahbi15-Jun-03 0:45 
GeneralRe: Limitation Pin
Mike Melnikov25-Jun-03 0:49
Mike Melnikov25-Jun-03 0:49 
Generalabout attribute and one advise Pin
marco.song13-May-03 16:04
marco.song13-May-03 16:04 
Generaljust get string, don't save to file. Pin
marco.song10-May-03 8:07
marco.song10-May-03 8:07 
GeneralRe: just get string, don't save to file. Pin
Mike Melnikov11-May-03 23:03
Mike Melnikov11-May-03 23:03 
GeneralRe: just get string, don't save to file. Pin
marco.song13-May-03 15:56
marco.song13-May-03 15:56 
GeneralProblem after fixing the memory leaks Pin
grebulon3-Apr-03 5:51
grebulon3-Apr-03 5:51 
GeneralQuestion about progam flow Pin
Pankaj Kumar7-Jan-03 19:51
Pankaj Kumar7-Jan-03 19:51 
Questionwhat about header? Pin
gok19-Dec-02 13:40
professionalgok19-Dec-02 13:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.