Watch how to download trading robots for free
Find us on Telegram!
Join our fan page
Interesting script?
So post a link to it -
let others appraise it
You liked the script? Try it in the MetaTrader 5 terminal
Libraries

EasyXML - XML Parser - library for MetaTrader 5

Views:
14288
Rating:
(54)
Published:
2013.12.25 11:47
Updated:
2016.11.22 07:32
\MQL5\Include\EasyXml\
easyxml.mqh (23.9 KB) view
easyxmlnode.mqh (7.68 KB) view
\MQL5\Scripts\EasyXml\
Need a robot or indicator based on this code? Order it on Freelance Go to Freelance

Main purpose and abilities

EasyXML is a simple yet powerful XML Parser which can read and parse XML from three different sources:

  1. URL
  2. File Input
  3. String Input

It is written completely in native MQL5 and relies on the Windows native "wininet.dll" only for fetching XML documents from an URL.

EasyXML will read XML as well as XHTML with (nearly) infinite node depth, as long as the document you are trying to parse is well-formed. It does not, however, validate the XML against a DTD or XSLT Stylesheet.


MQL5 Integration

EasyXML's Node Classes are inherited from the MQL5 native CObject and nodes are stored in a CArrayObj.

When walking the DOM tree nodes can be manipulated easily by using the public EasyXML methods as well as the MQL5 native functions to retrieve data from and store data to the DOM.

 

URL File Caching and Debugging

Since one can not always rely on RSS Feed Uptimes, EasyXML can store a XML cache file of the Feed, once it has loaded it from a URL successfully for the first time. The user then can use the cache file instead of the Live Feed for parsing, should the Feed be down for some reason.

Since XML and XHTML documents tend to be erroneous, EasyXML has a debugging option. While it can not repair broken XML, it surely will help to detect where the error is. If turned on it will print detailed information of the nodes parsed.

Besides that, any errors that occur will always be tracked and printed, despite the fact, if debugging is turned on or off.

 

Basic Usage

Just include the base class in your scripts and you're set up and ready to go:

//+------------------------------------------------------------------+
//| Includes                                                         |
//+------------------------------------------------------------------+
#include <EasyXML\EasyXml.mqh>

First, in your script, create an an instance of the EasyXML Class. Then set debugging and/or file caching and call one of the available methods to load the XML and start parsing:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   // Create instance of class CEasyXml
   CEasyXml EasyXmlDocument;

   // Optional debugging
   EasyXmlDocument.setDebugging(true);

   // Set Url Cache File
   EasyXmlDocument.setUrlCacheFile("forexcalendar.xml");

   // Method 1: Load XML from URL
   if(EasyXmlDocument.loadXmlFromUrl("http://www.forexfactory.com/ffcal_week_this.xml"))
     {
      readRecursive(EasyXmlDocument.getDocumentRoot());
     }

   // Clear the DOM
   EasyXmlDocument.Clear();

   // Method 2: Load XML from string
   if(EasyXmlDocument.loadXmlFromString("<root><child attr='value'>content</child><sibling>siblingcontent</sibling></root>"))
     {
      readRecursive(EasyXmlDocument.getDocumentRoot());
     }

   // Clear the DOM
   EasyXmlDocument.Clear();

   // Method 3: Load XML from file
   if(EasyXmlDocument.loadXmlFromFile("forexcalendar.xml"))
     {
      readRecursive(EasyXmlDocument.getDocumentRoot());
     }
  }

For demonstration purposes all of the three methods are shown. Normally you won't need all of them at once, although it is possible to clear the DOM tree in between and start parsing over again, even from another source. Just use the Clear() Command to erase the parsed DOM tree. setDebugging() and setUrlCacheFile() are optional and don't have to be called if they are not needed.

EasyXmlDocument.getDocumentRoot() will always return the root node of the DOM tree. All nodes including the root node are of type CEasyXmlNode, which itself derived from the MQL5 CObject (as mentioned before). From here on all of the methods of EasyXml as well as from CArrayObj and CObject may be used side by side to walk the parsed DOM tree.

The following example shows the implementation of readRecursive(), the global function which is called in the last code example:

//+------------------------------------------------------------------+
//| read xml recursive                                               |
//+------------------------------------------------------------------+
int readRecursive(CEasyXmlNode *ActualNode,int iNodeLevel=0)
  {
   // Output vars
   string sSpace;
   string sOutput;

   // Indent output for better readability
   StringInit(sSpace,iNodeLevel*4,StringGetCharacter(" ",0));

   // Concatenate output string
   sOutput += sSpace + IntegerToString(iNodeLevel) + " - Node Name: '" + ActualNode.getName() + "'";
   sOutput += (ActualNode.getValue()) ? " Value: '" + ActualNode.getValue() + "'" : "";

   // Iterate through AttributeNodes
   for(int i=0; i<ActualNode.Attributes().Total(); i++)
     {
      CEasyXmlAttribute *Attribute=ActualNode.Attributes().At(i);
      sOutput+=" || Attribute "+IntegerToString(i+1)+": '"+Attribute.getName()+"' Value: '"+Attribute.getValue()+"'";
     }

   Print(sOutput);

   // Iterate through child nodes
   for(int j=0; j<ActualNode.Children().Total(); j++)
     {
      CEasyXmlNode *ChildNode=ActualNode.Children().At(j);
      readRecursive(ChildNode,iNodeLevel+1);
     }

   return(0);
  }

Recursive reading of XML documents has big advantages over inline reading, although it may not be suitable for all needs. Calling Attributes() on a node will fetch all parsed Attributes while Children() will get the child nodes stored in the actual node. Both methods return a CArrayObj containing the elements. Calling Total() on those objects can be used in a for() loop to iterate over the elements. getName() and getValue() will return actual content stored in the node.

Of course it is possible to iterate over nodes inline as well:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// Create object of class CEasyXml
   CEasyXml EasyXmlDocument;

// Set debugging
   EasyXmlDocument.setDebugging(false);

// Example: Walking through the dom tree inline
   if(EasyXmlDocument.loadXmlFromUrl("http://www.forexfactory.com/ffcal_week_this.xml"))
     {
      CEasyXmlNode *RootNode=EasyXmlDocument.getDocumentRoot();

      //iterate through root node
      for(int i=0; i<RootNode.Children().Total(); i++)
        {
         CEasyXmlNode *ChildNode=RootNode.Children().At(i);
         Print(IntegerToString(i)+" "+ChildNode.getName());

         //iterate through child nodes
         for(int j=0; j<ChildNode.Children().Total(); j++)
           {
            CEasyXmlNode *SubNode=ChildNode.Children().At(j);
            Print(IntegerToString(i)+"-"+IntegerToString(j)+"   "+SubNode.getName()+" | "+SubNode.getValue());
           }
        }
     }
  }

Iteration works just as in the recursive example, except that a separate for() loop has to be established for each and every node level to be read.

Besides that it is also possible to walk the DOM step by step and manipulate single elements if needed:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// Create object of class CEasyXml
   CEasyXml EasyXmlDocument;

// Set debugging
   EasyXmlDocument.setDebugging(true);

// Example 2: Walking through the DOM tree step by step
   if(EasyXmlDocument.loadXmlFromString("<root><child attr='value'>content</child><sibling>siblingcontent</sibling></root>"))
     {
      CEasyXmlNode *Node=EasyXmlDocument.getDocumentRoot();
      Print(Node.getName());

      CEasyXmlNode *ChildNode=Node.FirstChild();
      Print(ChildNode.getName());

      // Always check for valid pointers if stepping sidewards manually.
      while(CheckPointer(ChildNode.Next())!=POINTER_INVALID)
        {
         ChildNode=ChildNode.Next();
         Print(ChildNode.getName());
        }

      CEasyXmlNode *ParentNode=ChildNode.Parent();
      Print(ParentNode.getName());

      // Back to root: ParentNode and Node are two different descriptors of the same object
      Print("Comparison of object descriptors: ParentNode == Node ? ",ParentNode==Node);
     }
  }

Here all of the available EasyXML methods as well as the native MQL5 Iteration/Getter/Setter of CObject and CArrayObj come in play.

Keep in mind, though, that some of those functions don't care about valid memory access and just return NULL, if they don't succeed.

In the last example calling ChildNode.Next() on the sibling node - without checking for pointer validity - would entail a serious bad pointer fault (= bad memory access), that will definitely crash the script. So if you ever have the need to step or manipulate the DOM tree manually, take care of pointer validity, as long as it concerns the CObject and CArrayObj class methods.


Most important node getters

MethodPurposeReturn
 Chilrden() Get all children of node CArrayObj - containing CEasyXmlNodes
 Attributes() Get all attributes of node CArrayObj - containing CEasyXmlAttributes
 Parent() Get parent node CEasyXmlNode (CObject)
 LastChild()  Get last node from children CEasyXmlNode (CObject)
 FirstChild() Get first node from children CEasyXmlNode (CObject)
 getName()  Get node name string
 getValue() Get node value (text content) string
 getAttribute(string pName)  Get Attribute by specified name string
 Next() (Inherited from CObject)  Get next sibling node CEasyXmlNode (CObject) || NULL
 Prev() (Inherited from CObject)  Get previous sibling node CEasyXmlNode (CObject) || NULL

 

Most important node setters

MethodPurposeReturn
 createChild(CEasyXmlNode *pChildNode) create new child node CEasyXmlNode (CObject) - the new child node
 createSibling(CEasyXmlNode *pSiblingNode) create new sibling node CEasyXmlNode (CObject) - the new sibling node
 setName(string pName)  set node name void
 setValue(string pValue) set node value (text content) void
 setAttribute(string pName,string pValue)  set new node attribute void

For more possibilities of dealing with nodes and node arrays please be sure to also read the documentation on CObject and CArrayObj.


Attribute getters/setters

Attribute Objects implements the same get/setName(), get/SetValue() methods to store and retrieve data just like the node objects.


Disclaimer

This piece of code is under active development, and, as with all software, does not claim to be free of bugs or other defects. Use EasyXml at your own risk and test thoroughly before implementing this library into any live trading EA. If you encounter any problems or have questions regarding the usage, please feel free to contact me.


Credits

The Integration of the wininet.dll used for fetching URL content uses WININET_TEST by Integer. Although this library is build upon its own, unique parsing system, the XML Parser written by yu-sha was a great learning source for dealing with MQL5 string operations.


Simple Display Panel Simple Display Panel

The code is designed to be a simple reference of how to create movable (drag) panels and populate it with information.

StepChoppy_v2 StepChoppy_v2

An indicator of trend power with eight states.

Fractals Modified Fractals Modified

This indicator is a small modification of classical Fractals Indicator. You can choose the number or left/right bars to have a new top or bottom as well a shift parameter.

Awesome Modified Awesome Modified

This indicator is a modification of classical Awesome indicator. It computes the smoothed rate of change of two exponential means.