Click here to Skip to main content
15,884,537 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 223.4K   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

 
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 
GeneralGreat Job :-) Pin
Ram. Sundar13-Nov-02 8:37
Ram. Sundar13-Nov-02 8:37 
GeneralMemory leaks Pin
9-Jul-02 0:28
suss9-Jul-02 0:28 
GeneralRe: Memory leaks Pin
John Kirk13-Sep-02 7:39
John Kirk13-Sep-02 7:39 
GeneralRe: Memory leaks Pin
Anonymous25-May-03 14:06
Anonymous25-May-03 14:06 
Generala couple of questions: Pin
Eugene Polonsky8-Jul-02 13:09
Eugene Polonsky8-Jul-02 13:09 
GeneralRe: a couple of questions: Pin
Mike Melnikov28-Aug-02 21:50
Mike Melnikov28-Aug-02 21:50 
Hello Eugene.

Sorry for delay. I just read your message Frown | :(

1. .h with .cpp
I guess that you put a lot of YOUR code in .cpp files and that is why don't like to see third party code there. You see, I put my code in .cpp Smile | :)
Without joke, it decreases compilation time.

2. Multithreaded mode
yes it is working in my 20 projects in Multithreaded mode. I don't make any CoInitialize in my "library" code.

3. deriving from a container
Yes is not good idea, but only for STL, I haven't read that book, but I think I will be near the author, because vector and etc. doesn't have virtual functions, may be some other issues. But I don't make any pointers to that vectors. You can use std::vector instead of vectordel, just don't forget to delete all stored pointers there.

4. typename in template
I wrote it to catch any pointer to a class , if _check(H ) work - very well.


Mike.
GeneralExtension for std::list's Pin
Thomas Freudenberg10-Jun-02 2:28
Thomas Freudenberg10-Jun-02 2:28 
GeneralNice package, but... Pin
Andreas Saurwein27-Feb-02 11:34
Andreas Saurwein27-Feb-02 11:34 
GeneralRe: Nice package, but... Pin
Mike Melnikov2-Mar-02 23:58
Mike Melnikov2-Mar-02 23:58 
GeneralRe: Nice package, but... Pin
Anonymous25-May-03 14:05
Anonymous25-May-03 14:05 
GeneralI am using expat to read the xml files, I need to get the value data to display through the web Pin
22-Feb-02 14:54
suss22-Feb-02 14:54 
QuestionHow to save new struct Pin
DanielVisual5-Sep-01 3:56
DanielVisual5-Sep-01 3:56 
AnswerRe: How to save new struct Pin
Mike Melnikov5-Sep-01 4:30
Mike Melnikov5-Sep-01 4:30 
GeneralThats what I need Pin
4-Sep-01 5:01
suss4-Sep-01 5:01 
GeneralPlease post a comment Pin
Mike Melnikov30-Aug-01 19:25
Mike Melnikov30-Aug-01 19:25 
GeneralRe: Please post a comment Pin
Christian Graus30-Aug-01 19:52
protectorChristian Graus30-Aug-01 19:52 
GeneralRe: Please post a comment Pin
Mike Melnikov5-Sep-01 4:24
Mike Melnikov5-Sep-01 4:24 
GeneralRe: Please post a comment Pin
14-Sep-01 5:05
suss14-Sep-01 5:05 
GeneralRe: Please post a comment Pin
14-Sep-01 5:06
suss14-Sep-01 5:06 

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.