- 显示:
- 2331
- 等级:
- 已发布:
- 2014.02.12 09:04
- 已更新:
- 2016.11.22 07:33
-
需要基于此代码的EA交易或指标吗?请在自由职业者服务中订购 进入自由职业者服务
主要目标和功能
EasyXML 是一个简单而强大的XML解析器, 它可以解析来自三个不同来源的XML:
- URL
- 文件输入
- 字符串输入
它完全使用MQL5语言并且只依赖于Windows自有的"wininet.dll"以从URL取得XML文档.
只要您尝试解析的文档是格式正确的, EasyXML 将(几乎)可以读取无穷节点深度的XML以及XHTML. 然而, 它并不根据DTD或者XSLT格式表检验XML.
MQL5 集成
EasyXML 的 Node 类是继承于MQL5本地CObject, 并且节点保存于CArrayObj.
当遍历DOM树时, 可以简单使用EasyXML的共用方法操作节点, 也可以使用MQL5内部函数在DOM中存取数据.
URL 文件缓存与调试
因为我们不能永远依赖于RSS数据流的在线可用, EasyXML可以在第一次成功从URL载入时为流文件保存一个XML缓存文件. 而用户可以在数据流由于某些原因无法实时访问时使用缓存文件进行解析.
因为 XML 和 XHTML 文档可能会出错, EasyXML 有调试选项. 尽管它不能修复损坏的 XML, 它可以帮助侦测错误位于哪里. 如果启动它, 它可以打印出当前解析的节点的详细信息.
另外无论调试选项是否开启, 任何错误都会被追踪和记录.
基本用法
只需要把基类文件包含在您的脚本中, 您就可以进一步使用了:
//+------------------------------------------------------------------+ //| 包含文件 | //+------------------------------------------------------------------+ #include <EasyXML\EasyXml.mqh>
首先, 在您的脚本中创建一个EasyXML类的实例. 然后设置调试和/或文件缓存, 然后调用一个可用的方法以载入XML文件并开始解析:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // 创建 CEasyXml 类的实例 CEasyXml EasyXmlDocument; // 可选调试 EasyXmlDocument.setDebugging(true); // 设置Url缓存文件 EasyXmlDocument.setUrlCacheFile("forexcalendar.xml"); // 方法 1: 从 URL 载入数据 if(EasyXmlDocument.loadXmlFromUrl("http://www.forexfactory.com/ffcal_week_this.xml")) { readRecursive(EasyXmlDocument.getDocumentRoot()); } // 清除 DOM EasyXmlDocument.Clear(); // 方法: 从字符串载入XML if(EasyXmlDocument.loadXmlFromString("<root><child attr='value'>content</child><sibling>siblingcontent</sibling></root>")) { readRecursive(EasyXmlDocument.getDocumentRoot()); } // 清除 DOM EasyXmlDocument.Clear(); // 方法 3: 从文件中载入 XML if(EasyXmlDocument.loadXmlFromFile("forexcalendar.xml")) { readRecursive(EasyXmlDocument.getDocumentRoot()); } }
为了演示的目的, 所有三种方法都显示了. 通常您不会同时需要所有方法, 尽管可以在其间清除DOM树并重新开始解析, 甚至解析另外来源的数据. 删除已经解析的DOM树, 只需要使用 Clear() 命令. setDebugging() 和 setUrlCacheFile() 是可选的, 如果没有必要可以不调用.
EasyXmlDocument.getDocumentRoot() 永远返回 DOM 树的根节点. 所有节点, 包括根节点, 类型都是 CEasyXmlNode, 它是从 MQL5 CObject 继承而来(前文已经提到). 自此, 所有EasyXml的方法以及来自CArrayObj和CObject的方法都可以用来遍历已然解析过的DOM树.
以下实例显示了 readRecursive() 的实现, 在最后的例子中将调用此全局函数:
//+------------------------------------------------------------------+ //| 递归读取xml | //+------------------------------------------------------------------+ int readRecursive(CEasyXmlNode *ActualNode,int iNodeLevel=0) { // 输出变量 string sSpace; string sOutput; // 缩进输出以获得更好可读性 StringInit(sSpace,iNodeLevel*4,StringGetCharacter(" ",0)); // 连接输出字符串 sOutput += sSpace + IntegerToString(iNodeLevel) + " - Node Name: '" + ActualNode.getName() + "'"; sOutput += (ActualNode.getValue()) ?" Value: '" + ActualNode.getValue() + "'" : ""; // 通过 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); // 迭代子节点 for(int j=0; j<ActualNode.Children().Total(); j++) { CEasyXmlNode *ChildNode=ActualNode.Children().At(j); readRecursive(ChildNode,iNodeLevel+1); } return(0); }
递归读取XML文档与按行读取相比有很大优势, 尽管它可能不适应于所有需求. 调用一个节点的 Attributes() 方法可以获取所有解析的属性, 而 Children() 可以获得此节点上存储的所有子节点. 这两个方法都返回包含元素的 CArrayObj 对象. 可以在for()循环中调用 Total() 来获得元素的数量. getName() 可以 getValue() 用于返回存储于节点的真实内容.
当然也可以在代码内如下直接迭代节点:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // 创建 CEasyXml 类对象 CEasyXml EasyXmlDocument; // 设置调试 EasyXmlDocument.setDebugging(false); // 例子: 嵌入代码遍历节点 if(EasyXmlDocument.loadXmlFromUrl("http://www.forexfactory.com/ffcal_week_this.xml")) { CEasyXmlNode *RootNode=EasyXmlDocument.getDocumentRoot(); //通过根节点迭代 for(int i=0; i<RootNode.Children().Total(); i++) { CEasyXmlNode *ChildNode=RootNode.Children().At(i); Print(IntegerToString(i)+" "+ChildNode.getName()); //迭代子节点 for(int j=0; j<ChildNode.Children().Total(); j++) { CEasyXmlNode *SubNode=ChildNode.Children().At(j); Print(IntegerToString(i)+"-"+IntegerToString(j)+" "+SubNode.getName()+" | "+SubNode.getValue()); } } } }
迭代和递归例子类似, 除了使用for()循环读取每个节点的内容.
另外它还可以通过一步一步遍历DOM来按需要操作单独的元素:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // 创建 CEasyXml 类对象 CEasyXml EasyXmlDocument; // 设置调试 EasyXmlDocument.setDebugging(true); // 实例 2: 一步一步遍历DOM数 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()); // 永远检查指针的正确性. while(CheckPointer(ChildNode.Next())!=POINTER_INVALID) { ChildNode=ChildNode.Next(); Print(ChildNode.getName()); } CEasyXmlNode *ParentNode=ChildNode.Parent(); Print(ParentNode.getName()); // 回到根: 父节点以及相同对象有两个描述符的节点 Print("Comparison of object descriptors: ParentNode == Node ?",ParentNode==Node); } }
这样, 所有 EasyXML 的可用方法以及 MQL5 本地CObject和CArrayObj迭代/读取/设置都可以使用了.
但是请记住, 这些函数中的一部分不会处理内存访问的有效性, 如果不成功, 它们只会返回NULL.
在以上兄弟节点部分调用ChildNode.Next() 的实例中 - 没有检验指针的有效性 - 可能会引起严重的无效指针错误 (= 无效内存访问), 可能会使脚本崩溃. 所以如果您需要人工分步操作DOM树, 请注意指针有效性, 因为它关乎CObject和CArrayObj的类方法.
最重要的节点获取函数
方法 | 目标 | 返回 |
---|---|---|
Chilrden() | 取得节点的所有子节点 | CArrayObj - 包含了 CEasyXmlNodes |
Attributes() | 取得节点的所有属性 | CArrayObj - 包含了 CEasyXmlAttributes |
Parent() | 取得父节点 | CEasyXmlNode (CObject) |
LastChild() | 取得最后一个子节点 | CEasyXmlNode (CObject) |
FirstChild() | 取得第一个子节点 | CEasyXmlNode (CObject) |
getName() | 取得节点名称 | string |
getValue() | 取得节点值 (文本内容) | string |
getAttribute(string pName) | 根据指定名称取得属性 | string |
Next() (继承于 CObject) | 取得下一个兄弟节点 | CEasyXmlNode (CObject) || NULL |
Prev() (继承于 CObject) | 取得前一个兄弟节点 | CEasyXmlNode (CObject) || NULL |
最重要的节点设置函数
方法 | 目标 | 返回 |
---|---|---|
createChild(CEasyXmlNode *pChildNode) | 创建新的子节点 | CEasyXmlNode (CObject) - 新子节点 |
createSibling(CEasyXmlNode *pSiblingNode) | 创建新的兄弟节点 | CEasyXmlNode (CObject) - 新的兄弟节点 |
setName(string pName) | 设置节点名称 | void |
setValue(string pValue) | 设置节点值 (文本内容) | void |
setAttribute(string pName,string pValue) | 设置新的节点属性 | void |
属性读取函数/设置函数
属性对象和节点对象类似, 都提供了 get/setName(), get/SetValue() 方法以存取数据.
声明
这些代码还在开发过程中, 与所有软件类似, 不能说没有错误或者缺陷. 在任何交易EA交易中使用此函数库, 需要您自己管理风险并作完全测试. 如果您遇到任何困难或者在使用中有问题, 请尽管联系我.
开发组
与 wininet.dll 集成以获取URL内容的部分使用了 WININET_TEST, 作者 Integer. 尽管这个库是独立实现的解析系统, XML Parser, 作者yu-sha, 是处理MQL5字符串操作的很好的学习资源.
由MetaQuotes Ltd译自英文
原代码: https://www.mql5.com/en/code/1998