XML/XPath: Difference between revisions

3,893 bytes added ,  4 years ago
→‎{{header|C++}}: Replaced broken example with one that actually uses XPath.
(add task to ARM64 assembly Raspberry Pi)
(→‎{{header|C++}}: Replaced broken example with one that actually uses XPath.)
Line 832:
 
=={{header|C++}}==
 
{{improve|C++|Does not use XPath}}
The following code uses each of the three tasks given to demonstrate a different method of handling the resulting node set from an XPath query.
<lang cpp>#include <vector>
 
#include <string>
{{libheader|LibXML}}
<lang cpp>#include <cassert>
#include <cstdlib>
#include <iostream>
#include <boost/regex.hppstdexcept>
#include <algorithmutility>
#include <iteratorvector>
 
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlerror.h>
#include <libxml/xmlstring.h>
#include <libxml/xmlversion.h>
#include <libxml/xpath.h>
 
#ifndef LIBXML_XPATH_ENABLED
# error libxml was not configured with XPath support
#endif
 
// Because libxml2 is a C library, we need a couple things to make it work
// well with modern C++:
// 1) a ScopeGuard-like type to handle cleanup functions; and
// 2) an exception type that transforms the library's errors.
 
// ScopeGuard-like type to handle C library cleanup functions.
template <typename F>
class [[nodiscard]] scope_exit
{
public:
// C++20: Constructor can (and should) be [[nodiscard]].
/*[[nodiscard]]*/ constexpr explicit scope_exit(F&& f) :
f_{std::move(f)}
{}
 
~scope_exit()
{
f_();
}
 
// Non-copyable, non-movable.
scope_exit(scope_exit const&) = delete;
scope_exit(scope_exit&&) = delete;
auto operator=(scope_exit const&) -> scope_exit& = delete;
auto operator=(scope_exit&&) -> scope_exit& = delete;
 
private:
F f_;
};
 
// Exception that gets last libxml2 error.
class libxml_error : public std::runtime_error
{
public:
libxml_error() : libxml_error(std::string{}) {}
 
explicit libxml_error(std::string message) :
std::runtime_error{make_message_(std::move(message))}
{}
 
private:
static auto make_message_(std::string message) -> std::string
{
if (auto const last_error = ::xmlGetLastError(); last_error)
{
if (not message.empty())
message += ": ";
message += last_error->message;
}
 
return message;
}
};
 
auto main() -> int
{
try
{
// Initialize libxml.
::xmlInitParser();
LIBXML_TEST_VERSION
auto const libxml_cleanup = scope_exit{[] { ::xmlCleanupParser(); }};
 
// Load and parse XML document.
auto const doc = ::xmlParseFile("test.xml");
if (not doc)
throw libxml_error{"failed to load document"};
auto const doc_cleanup = scope_exit{[doc] { ::xmlFreeDoc(doc); }};
 
// Create XPath context for document.
auto const xpath_context = ::xmlXPathNewContext(doc);
if (not xpath_context)
throw libxml_error{"failed to create XPath context"};
auto const xpath_context_cleanup = scope_exit{[xpath_context]
{ ::xmlXPathFreeContext(xpath_context); }};
 
// Task 1 ============================================================
{
std::cout << "Task 1:\n";
 
auto const xpath =
reinterpret_cast<::xmlChar const*>(u8"//item[1]");
 
// Create XPath object (same for every task).
auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context);
if (not xpath_obj)
throw libxml_error{"failed to evaluate XPath"};
auto const xpath_obj_cleanup = scope_exit{[xpath_obj]
{ ::xmlXPathFreeObject(xpath_obj); }};
 
// 'result' a xmlNode* to the desired node, or nullptr if it
// doesn't exist. If not nullptr, the node is owned by 'doc'.
auto const result = xmlXPathNodeSetItem(xpath_obj->nodesetval, 0);
if (result)
std::cout << '\t' << "node found" << '\n';
else
std::cout << '\t' << "node not found" << '\n';
}
 
// Task 2 ============================================================
{
std::cout << "Task 2:\n";
 
auto const xpath =
reinterpret_cast<::xmlChar const*>(u8"//price/text()");
 
// Create XPath object (same for every task).
auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context);
if (not xpath_obj)
throw libxml_error{"failed to evaluate XPath"};
auto const xpath_obj_cleanup = scope_exit{[xpath_obj]
{ ::xmlXPathFreeObject(xpath_obj); }};
 
// Printing the results.
auto const count =
xmlXPathNodeSetGetLength(xpath_obj->nodesetval);
for (auto i = decltype(count){0}; i < count; ++i)
{
auto const node =
xmlXPathNodeSetItem(xpath_obj->nodesetval, i);
assert(node);
 
auto const content = XML_GET_CONTENT(node);
assert(content);
 
// Note that reinterpret_cast here is a Bad Idea, because
// 'content' is UTF-8 encoded, which may or may not be the
// encoding cout expects. A *PROPER* solution would translate
// content to the correct encoding (or at least verify that
// UTF-8 *is* the correct encoding).
//
// But this "works" well enough for illustration.
std::cout << "\n\t" << reinterpret_cast<char const*>(content);
}
 
std::cout << '\n';
}
 
// Task 3 ============================================================
{
std::cout << "Task 3:\n";
 
auto const xpath =
reinterpret_cast<::xmlChar const*>(u8"//name");
 
// Create XPath object (same for every task).
auto xpath_obj = ::xmlXPathEvalExpression(xpath, xpath_context);
if (not xpath_obj)
throw libxml_error{"failed to evaluate XPath"};
auto const xpath_obj_cleanup = scope_exit{[xpath_obj]
{ ::xmlXPathFreeObject(xpath_obj); }};
 
// 'results' is a vector of pointers to the result nodes. The
int main( ) {
// nodes pointed to are owned by 'doc'.
const std::string xmltext(
auto const results = [ns=xpath_obj->nodesetval]()
"<inventory title=\"OmniCorp Store #45x10^3\">"
{
"<section name=\"health\">"
auto v = std::vector<::xmlNode*>{};
"<item upc=\"123456789\" stock=\"12\">"
if (ns && ns->nodeTab)
"<name>Invisibility Cream</name>"
v.assign(ns->nodeTab, ns->nodeTab + ns->nodeNr);
"<price>14.50</price>"
return v;
"<description>Makes you invisible</description>"
}();
"</item>"
std::cout << '\t' << "set of " << results.size()
"<item upc=\"445322344\" stock=\"18\">"
<< " node(s) found" << '\n';
"<name>Levitation Salve</name>"
}
"<price>23.99</price>"
}
"<description>Levitate yourself for up to 3 hours per application</description>"
catch (std::exception const& x)
"</item>"
{
"</section>"
std::cerr << "ERROR: " << x.what() << '\n';
"<section name=\"food\">"
return EXIT_FAILURE;
"<item upc=\"485672034\" stock=\"653\">"
}
"<name>Blork and Freen Instameal</name>"
}
"<price>4.95</price>"
</lang>
"<description>A tasty meal in a tablet; just add water</description>"
"</item>"
"<item upc=\"132957764\" stock=\"44\">"
"<name>Grob winglets</name>"
"<price>3.56</price>"
"<description>Tender winglets of Grob. Just add water</description>"
"</item>"
"</section>"
"</inventory>" ) ;
std::string::size_type found = xmltext.find( "<item" , 0 ) ; //beginning of first item
std::string::size_type foundnext = xmltext.find( "</item>" , found + 5 ) ; //and its end
std::cout << "The first item is\n" << xmltext.substr( found + 5 , foundnext - ( found + 5 ) ) << '\n' ;
std::string::const_iterator start , end ;
start = xmltext.begin( ) ;
end = xmltext.end( ) ;
boost::match_results<std::string::const_iterator> what ;
boost::regex pricefind( "<price>(\\d+\\.?\\d+)</price>" ) ;//this regex finds the prices
start = xmltext.begin( ) ;
std::cout << "The prices are:\n" ;
while ( boost::regex_search( start , end , what , pricefind ) ) {
std::string price( what[ 1 ].first , what[ 1 ].second ) ;//find the first price
std::cout << price << std::endl ;
start = what[ 1 ].second ; //continue search after first price found
}
start = xmltext.begin( ) ;
std::vector<std::string> names ;
boost::regex namefind( "<name>(.+?)</name>" ) ; //find characters, be greedy!
while ( boost::regex_search ( start , end , what , namefind ) ) {
std::string name ( what[ 1 ].first , what[ 1 ].second ) ;
names.push_back( name ) ;
start = what[ 1 ].second ;
}
std::cout << "The following name elements were found in the xml string:\n" ;
std::copy( names.begin( ) , names.end( ) , std::ostream_iterator<std::string>( std::cout , "\n" )) ;
return 0 ;
}</lang>
 
=={{header|Caché ObjectScript}}==
12

edits