parsing xml having multiple namespaces using xml::Libxml::Xpathcontext in perl - perl

I have to parse following XML:
<reply xmlns="urn::ietf::param" xmlns:element="https://xml.example.net/abc/12.3"
message-id='1'>
<abc>
<xyz> hello </xyz>
</abc>
</reply>
I want the value of xyz node i.e. hello, but findnodes is returning null value.
my code is :
my $xpath=XML::LibXML::XPathContext->new($dom);
$xpath->registerNs('ns1','urn::ietf::param');
$xpath->registerNs('ns2','https://xml.example.net/abc/12.3');
$val=$xpath->findnodes('//ns1:reply/ns2:element/abc/xyz');
print $val;
but print statement is returning null value

It looks like you have some misunderstanding about how namespaces work. xmlns:element="https://xml.example.net/abc/12.3" means that there is a prefix element defined with this specific namespace URI. This namespace is actually never used in your XML.
xmlns="urn::ietf::param" defines a default namespaces and also applies to all descendant elements.
And actually you have no <element/> element in your XML.
Thus, the following XPath should work as expected:
//ns1:reply/ns1:abc/ns1:xyz

Related

XML Expression Binding - Proceed Code in conditional operator

I am currently working on a Fiori app. At the moment I try to set a title depending on the value of a property I get from my OData service. Therefore I want to use expression binding with the conditional operator.
So when ${PROPERTIY} has the value "EXAMPLE", it should print the value of OUTPUT_PROPERTY_1. Otherwise, it should print the value of OUTPUT_PROPERTY_2.
XML:
<ObjectListItem title="{= ${PROPERTIY} === 'EXAMPLE' ? '${OUTPUT_PROPERTY_1}' : '${OUTPUT_PROPERTY_2}'}">
Unfortunately, it just prints ${OUTPUT_PROPERTY_1} or ${OUTPUT_PROPERTY_2}, and does not proceed the code to get the actual value of the properties.
Is there any chance to solve this problem or even a good workaround in order to print the actual value of the related property?
Remove the apostrophes around the expression binding syntax:
title="{= ${PROPERTIY} === 'EXAMPLE' ? ${OUTPUT_PROPERTY_1} : ${OUTPUT_PROPERTY_2}}"
Otherwise, '${OUTPUT_PROPERTY_x}' will be treated as a string literal.

Codeception - how to check response doesn't match xpath

I have the following problem - I want to test REST api with Codeception.
When I want to make sure in JSON response I have array:
'data' => [
'sth' => 'whathever'
]
I can use seeResponseJsonMatchesXpath this way:
$I->seeResponseJsonMatchesXpath('//data/sth');
But what to do if I want to make sure I don't have in response sth? I would like to use for example:
$I->seeResponseJsonMatchesXpath('//data');
$I->dontSeeResponseJsonMatchesXpath('//data/sth');
but obviously there is no dontSeeResponseJsonMatchesXpath method in Codeception.
I also don't know what will be exact value in sth so probably I cannot use here dontSeeResponseContainsJson.
The question is - how to make the following check?
(Disclaimer: Entirely unfamiliar with codeception, only with XPath)
JSON is not supposed to be checked against XPath. But the check you could probably do is
$I->seeResponseJsonMatchesXpath('not(//data/sth)');
Which would return true or 1 if there is no data and sth - and false or 0 if there is.
EDIT: As a response to your comments:
Probably this is something close I need but I want to make sure only sth is not present inside data whereas data should exist (but I don't need to check it in this expression)
The expression already does exactly that - not(//data/sth) returns false for a document like
<data>
<sth/>
</data>
but returns true for a document containing data only:
<data/>
or anything else. But it seems to me there could be pitfalls with converting JSON to XML.
I checked both solutions - yours and //data/not(sth) but it doesn't seem to work
Yes, that does not work because you are using an XPath 1.0 engine. Your attempt would be a valid XPath 2.0 expression. Use the following expression to independently test data and sth:
//data and not(//data/sth)
This only returns true if at least one data element exists and if there is no data element that has an sth element as a child.

Why is this xmlns attribute messing up my xpath query?

I'm parsing a simple jhove output using LibXML. However, I don't get the values I expect. Here's the code:
use feature "say";
use XML::LibXML;
my $PRSR = XML::LibXML->new();
my $xs=<DATA>;
say $xs;
my $t1 = $PRSR->load_xml(string => $xs);
say "1:" . $t1->findvalue('//date');
$xs=<DATA>;
say $xs;
$t1 = $PRSR->load_xml(string => $xs);
say "2:" . $t1->findvalue('//date');
__DATA__
<jhove xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://hul.harvard.edu/ois/xml/ns/jhove" xsi:schemaLocation="http://hul.harvard.edu/ois/xml/ns/jhove http://hul.harvard.edu/ois/xml/xsd/jhove/1.3/jhove.xsd" name="Jhove" release="1.0 (beta 3)" date="2005-02-04"><date>2006-10-06T09:11:34+02:00</date></jhove>
<jhove><date>2006-10-06T09:11:34+02:00</date></jhove>
As you can see, the line "1:" is returning an empty string, while "2:" is returning the expected date. What is in the jhove-root-element that keeps the xpath query from working properly? I even tried in XML-Spy and there it works, even with the full header.
Edit: When I remove the xmlns-attribute from the root element, the xpath query works. But how is that possible?
The XML::LibXML::Node documentation specifically mentions this issue and how to deal with it...
NOTE ON NAMESPACES AND XPATH:
A common mistake about XPath is to assume that node tests consisting of an element name with no prefix match elements in the default namespace. This assumption is wrong - by XPath specification, such node tests can only match elements that are in no (i.e. null) namespace.
So, for example, one cannot match the root element of an XHTML document with $node->find('/html') since '/html' would only match if the root element <html> had no namespace, but all XHTML elements belong to the namespace http://www.w3.org/1999/xhtml. (Note that xmlns="..." namespace declarations can also be specified in a DTD, which makes the situation even worse, since the XML document looks as if there was no default namespace).
There are several possible ways to deal with namespaces in XPath:
The recommended way is to use the XML::LibXML::XPathContext module to define an explicit context for XPath evaluation, in which a document independent prefix-to-namespace mapping can be defined. For example:
my $xpc = XML::LibXML::XPathContext->new;
$xpc->registerNs('x', 'http://www.w3.org/1999/xhtml');
$xpc->find('/x:html',$node);
Another possibility is to use prefixes declared in the queried document (if known). If the document declares a prefix for the namespace in question (and the context node is in the scope of the declaration), XML::LibXML allows you to use the prefix in the XPath expression, e.g.:
$node->find('/x:html');
I found another solution. Simply using this
say "1:" . $t1->findvalue('//*[local-name()="date"]');
will also find the value and save the hassle of declaring namespaces in an XPathContext. But apart from that, tobyinks answer is the correct one.

Perl LibXML: isSameNode method

I'm trying to compare two nodes using 'isSameNode'. However, one of the nodes is created via 'parse_balanced_chunk'. From the http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isSameNode document, is says "This method provides a way to determine whether two Node references returned by the implementation reference the same object."
So I'm wonder is it not working as I would expect because they are indeed coming from two different sources (one from the parsed txt file, the other from parse_balance_chunk)?
Here is the 'test_in.xml' & code:
<?xml version="1.0"?>
<TT>
<A>ZAB</A>
<B>ZBW</B>
<C>
<D>
<E>ZSE</E>
<F>ZLC</F>
</D>
</C>
<C>
<K>
<H>dog</H>
<E>one123</E>
<M>bird</M>
</K>
</C>
</TT>
use warnings;
use strict;
use XML::LibXML;
my $parser = XML::LibXML->new({keep_blanks=>(0)});
my $dom = $parser->load_xml(location => 'test_in.xml') or die;
#called in scalar context ($na1)
my ($na1) = $dom->findnodes('//D');
my ($na2) = $dom->findnodes('//D');
my $X1 = $na1->isSameNode($na2); ##MATCHES
my ($frg) = $parser->parse_balanced_chunk ("<D><E>ZSE</E><F>ZLC</F></D>");
my $X2 = $na1->isSameNode($frg); ##WHY NO MATCH?
my ($na3) = $frg->findnodes('//D');
my $X3 = $na1->isSameNode($na3); ##WHY NO MATCH?
print "SAME?: $X1\n";
print "SAME?: $X2\n";
print "SAME?: $X3\n";
And the output:
SAME?: 1
SAME?: 0
SAME?: 0
So the first 'isSameNode' test obviously MATCHES (same exact findnodes & xpath expression).
But neither of the 2nd or 3rd 'isSameNode' tests work using the node from the 'parsed_balance_chunk'. Is it something simple I'm overlooking with the syntax or is it just that I can't compare two nodes this way? If not, what is the method for comparing two nodes? Waht I'm ultimately trying to determine if a block of xml code (i.e. from a previous parsed_balance_chuck) already exist in the xml file.
Because they're not the same node. Like the name says, it checks if two nodes are the same node. You seem think think it checks if two nodes are equivalent, but that's not what it does.

libxml2 - get node(xmlNodePtr) content?

I parsed and have pointer xmlNodePtr upto category tag, But I want to get the value of the node(name) like TrailersFreeMovies , Trailers in an array.
<categories><category><name>TrailersFreeMovies</name><url>https://www.ex1.com/srs/index.php?cid=47</url></category><category><name>Trailers</name><url>https://www.ex1.com/srs/index.php?cid=45</url></category></categories>
guide me to parse this
XPath part of the api returns an array of nodes. See XPath examples.
Once you obtain the result of xmlXPathEvalExpression as xpathObj then the array is in xpathObj->nodesetval->nodeTab. nodesetval is a pointer to xmlNodeSet type.
xmlGetNodePath returns following values for the nodes of your sample xml matching the //name xpath expression:
/categories/category[1]/name
/categories/category[2]/name
So a specific answer to your question would be: apply xpath expression ("%s/name", xmlGetNodePath(categoryNode)) and process returned array of nodes. For each entry get the text with xmlNodeListGetString(doc, node->xmlChildrenNode, 1).