xpath1.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. (**
  2. * section: XPath
  3. * synopsis: Evaluate XPath expression and prints result node set.
  4. * purpose: Shows how to evaluate XPath expression and register
  5. * known namespaces in XPath context.
  6. * usage: xpath1 <xml-file> <xpath-expr> [<known-ns-list>]
  7. * test: xpath1 test3.xml '//child2' > xpath1.tmp && diff xpath1.tmp $(srcdir)/xpath1.res
  8. * author: Aleksey Sanin
  9. * copy: see Copyright for the status of this software.
  10. *)
  11. program xpath1;
  12. {$mode objfpc}
  13. uses
  14. ctypes,
  15. xml2,
  16. exutils,
  17. SysUtils;
  18. (**
  19. * usage:
  20. * @name: the program name.
  21. *
  22. * Prints usage information.
  23. *)
  24. procedure usage(const name: String);
  25. begin
  26. //assert(name <> '');
  27. printfn('Usage: %s <xml-file> <xpath-expr> [<known-ns-list>]', [name]);
  28. printfn('where <known-ns-list> is a list of known namespaces');
  29. printfn('in "<prefix1>=<href1> <prefix2>=href2> ..." format');
  30. end;
  31. (**
  32. * print_xpath_nodes:
  33. * @nodes: the nodes set.
  34. * @output: the output file handle.
  35. *
  36. * Prints the @nodes content to @output.
  37. *)
  38. procedure print_xpath_nodes(nodes: xmlNodeSetPtr; var output: TextFile);
  39. var
  40. cur: xmlNodePtr;
  41. size: cint;
  42. i: cint;
  43. ns: xmlNsPtr;
  44. begin
  45. //assert(output);
  46. if nodes <> Nil then
  47. size := nodes^.nodeNr
  48. else
  49. size := 0;
  50. WriteLn(output, Format('Result (%d nodes):', [size]));
  51. for i := 0 to size - 1 do
  52. begin
  53. assert(nodes^.nodeTab[i] <> Nil);
  54. if nodes^.nodeTab[i]^._type = XML_NAMESPACE_DECL then
  55. begin
  56. ns := xmlNsPtr(nodes^.nodeTab[i]);
  57. cur := xmlNodePtr(ns^.next);
  58. if cur^.ns <> nil then
  59. WriteLn(output, Format('= namespace "%s"="%s" for node %s:%s',
  60. [ns^.prefix, ns^.href, cur^.ns^.href, cur^.name]))
  61. else
  62. WriteLn(output, Format('= namespace "%s"="%s" for node %s',
  63. [ns^.prefix, ns^.href, cur^.name]));
  64. end
  65. else if nodes^.nodeTab[i]^._type = XML_ELEMENT_NODE then
  66. begin
  67. cur := nodes^.nodeTab[i];
  68. if cur^.ns <> Nil then
  69. WriteLn(output, Format('= element node "%s:%s"',
  70. [cur^.ns^.href, cur^.name]))
  71. else
  72. WriteLn(output, Format('= element node "%s"', [cur^.name]));
  73. end
  74. else
  75. begin
  76. cur := nodes^.nodeTab[i];
  77. WriteLn(output, Format('= node "%s": type %d', [cur^.name, cur^._type]));
  78. end;
  79. end;
  80. end;
  81. (**
  82. * register_namespaces:
  83. * @xpathCtx: the pointer to an XPath context.
  84. * @nsList: the list of known namespaces in
  85. * "<prefix1>=<href1> <prefix2>=href2> ..." format.
  86. *
  87. * Registers namespaces from @nsList in @xpathCtx.
  88. *
  89. * Returns 0 on success and a negative value otherwise.
  90. *)
  91. function register_namespaces(xpathCtx: xmlXPathContextPtr; const nsList: xmlCharPtr): cint;
  92. var
  93. nsListDup: xmlCharPtr;
  94. prefix: xmlCharPtr;
  95. href: xmlCharPtr;
  96. next: xmlCharPtr;
  97. begin
  98. assert(xpathCtx <> Nil);
  99. assert(nsList <> Nil);
  100. nsListDup := xmlStrdup(nsList);
  101. if nsListDup = Nil then
  102. begin
  103. printfn('Error: unable to strdup namespaces list');
  104. Exit(-1);
  105. end;
  106. next := nsListDup;
  107. while next <> Nil do
  108. begin
  109. (* skip spaces *)
  110. while next^ = ' ' do
  111. Inc(next);
  112. if next^ = #0 then
  113. break;
  114. (* find prefix *)
  115. prefix := next;
  116. next := xmlCharPtr(xmlStrchr(next, '='));
  117. if next = Nil then
  118. begin
  119. printfn('Error: invalid namespaces list format');
  120. xmlFree(nsListDup);
  121. Exit(-1);
  122. end;
  123. next^ := #0;
  124. Inc(next);
  125. (* find href *)
  126. href := next;
  127. next := xmlCharPtr(xmlStrchr(next, ' '));
  128. if next <> Nil then
  129. begin
  130. next^ := #0;
  131. Inc(next);
  132. end;
  133. (* do register namespace *)
  134. if xmlXPathRegisterNs(xpathCtx, prefix, href) <> 0 then
  135. begin
  136. printfn('Error: unable to register NS with prefix="%s" and href="%s"', [prefix, href]);
  137. xmlFree(nsListDup);
  138. Exit(-1);
  139. end;
  140. end;
  141. xmlFree(nsListDup);
  142. Result := 0;
  143. end;
  144. (**
  145. * execute_xpath_expression:
  146. * @filename: the input XML filename.
  147. * @xpathExpr: the xpath expression for evaluation.
  148. * @nsList: the optional list of known namespaces in
  149. * "<prefix1>=<href1> <prefix2>=href2> ..." format.
  150. *
  151. * Parses input XML file, evaluates XPath expression and prints results.
  152. *
  153. * Returns 0 on success and a negative value otherwise.
  154. *)
  155. function execute_xpath_expression(const filename: PAnsiChar; const xpathExpr, nsList: xmlCharPtr): cint;
  156. var
  157. doc: xmlDocPtr;
  158. xpathCtx: xmlXPathContextPtr;
  159. xpathObj: xmlXPathObjectPtr;
  160. begin
  161. assert(filename <> Nil);
  162. assert(xpathExpr <> Nil);
  163. (* Load XML document *)
  164. doc := xmlParseFile(filename);
  165. if doc = Nil then
  166. begin
  167. printfn('Error: unable to parse file "%s"', [filename]);
  168. Exit(-1);
  169. end;
  170. (* Create xpath evaluation context *)
  171. xpathCtx := xmlXPathNewContext(doc);
  172. if xpathCtx = Nil then
  173. begin
  174. printfn('Error: unable to create new XPath context');
  175. xmlFreeDoc(doc);
  176. Exit(-1);
  177. end;
  178. (* Register namespaces from list (if any) *)
  179. if (nsList <> Nil) and (register_namespaces(xpathCtx, nsList) < 0) then
  180. begin
  181. printfn('Error: failed to register namespaces list "%s"', [nsList]);
  182. xmlXPathFreeContext(xpathCtx);
  183. xmlFreeDoc(doc);
  184. Exit(-1);
  185. end;
  186. (* Evaluate xpath expression *)
  187. xpathObj := xmlXPathEvalExpression(xpathExpr, xpathCtx);
  188. if xpathObj = Nil then
  189. begin
  190. printfn('Error: unable to evaluate xpath expression "%s"', [xpathExpr]);
  191. xmlXPathFreeContext(xpathCtx);
  192. xmlFreeDoc(doc);
  193. Exit(-1);
  194. end;
  195. (* Print results *)
  196. print_xpath_nodes(xpathObj^.nodesetval, StdOut);
  197. (* Cleanup *)
  198. xmlXPathFreeObject(xpathObj);
  199. xmlXPathFreeContext(xpathCtx);
  200. xmlFreeDoc(doc);
  201. Result := 0;
  202. end;
  203. begin
  204. (* Parse command line and process file *)
  205. if (ParamCount < 2) or (ParamCount > 3) then
  206. begin
  207. printfn('Error: wrong number of arguments.');
  208. usage(ParamStr(0));
  209. Halt(-1);
  210. end;
  211. (* Init libxml *)
  212. xmlInitParser();
  213. LIBXML_TEST_VERSION;
  214. (* Do the main job *)
  215. if execute_xpath_expression(PAnsiChar(ParamStr(1)), PAnsiChar(ParamStr(2)),
  216. specialize IfThen<PAnsiChar>(argc > 2, PAnsiChar(ParamStr(3)), Nil)) < 0 then
  217. begin
  218. usage(ParamStr(0));
  219. Halt(-1);
  220. end;
  221. (* Shutdown libxml *)
  222. xmlCleanupParser();
  223. (*
  224. * this is to debug memory for regression tests
  225. *)
  226. xmlMemoryDump();
  227. end.