XPath in PHPUnit testing with Zend - unit-testing

I have this apparently basic code:
$docSrc =
'<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title /></head>
<body><p>Test paragraph.</p></body>
</html>';
$domDoc = new DOMDocument();
$domDoc->loadXML($docSrc);
$xpath = new DOMXPath($domDoc);
$nodeList = $xpath->query('//p');
$this->assertTrue($nodeList->length == 1);
It should succeed, but fails miserably, the length is 0. I have been looking for a solution all day, but to no avail.
Wow thanks! It works! Unfortunately, my original code:
$query = new Zend_Dom_Query($docSrc);
$xpathQuery = '//p';
$result = $query->queryXpath($xpathQuery);
$this->assertTrue($result->count() == 1);
thinks it is an XML and perform loadXML. Do you have any idea why this happens?
Ok I found the culprit somewhere in the Zend Library:
if ('<' . '?xml' == substr(trim($document), 0, 5)) {
return $this->setDocumentXml($document, $encoding);
}
I wonder if this is correct since it IS an XML document, but load XML does not work.
Ok I’m doing some research. Apparently, the problem has something to do with namespaces...

Change line 2 to:
$domDoc->loadHTML($docSrc);

There IS a bug, workaround is:
$docSrc =
'<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title /></head>
<body><p>Test paragraph.</p></body>
</html>';
$domDoc = new DOMDocument();
$domDoc->loadXML($docSrc);
$xpath = new DOMXPath($domDoc);
$xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
$nodeList = $xpath->query('//xhtml:p');
$this->assertTrue($nodeList->length == 1);

Related

PHP DomXPath - "object value omitted" even if new DomDocument is not empty?

I have a xml file like this...
XML File
<?xml version="1.0" encoding="UTF-8"?>
<database xmlns="tt:foo" xmlns:h4a="https://www.myexample.com/foo.xsd" >
<tables>
<table name="my_first_table_name">
<column name="column_name_1" />
</table>
</tables>
</database>
I 'm trying to use DOMXPath for a future xml merging.
PHP Code
$doc_ref = new \DOMDocument();
$doc_ref->load( $array_xml_paths[0] );
$xpath_ref = new \DOMXPath($doc_ref);
error_log_array( $xpath_ref );
foreach ($xpath_ref->evaluate('//table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
output
[08-Mar-2018 14:21:47 UTC] DOMXPath Object
(
[document] => (object value omitted)
)
The DOMDocument is correclty loaded, but something more seems to be necessary for DOMXPath to work it but I don't know.
First of all...
[document] => (object value omitted) DOES NOT MEAN the DOMXPath instance is empty !
I found the solution. It was in this comment.
I had to register the xml namespace to use evaluate().
Solution
$doc_ref = new \DOMDocument();
$doc_ref->load( $array_xml_paths[0] );
$xpath_ref = new \DOMXPath( $doc_ref );
$ns = $doc_ref->documentElement->namespaceURI;
if($ns) {
$xpath_ref->registerNamespace("ns", $ns);
foreach ($xpath_ref->evaluate('//ns:table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
}else {
foreach ($xpath_ref->evaluate('//table[#name]') as $table) {
$table_name = $table->getAttribute('name');
error_log( $table_name );
}
}

Xerces XPath causes seg fault when path doesn't exist

I can successfully use Xerces XPath feature to query for information from an XML with the following XML and C++ code.
XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<ApplicationSettings>
hello universe
</ApplicationSettings>
</root>
C++
int main()
{
XMLPlatformUtils::Initialize();
// create the DOM parser
XercesDOMParser *parser = new XercesDOMParser;
parser->setValidationScheme(XercesDOMParser::Val_Never);
parser->parse("fake_cmf.xml");
// get the DOM representation
DOMDocument *doc = parser->getDocument();
// get the root element
DOMElement* root = doc->getDocumentElement();
// evaluate the xpath
DOMXPathResult* result=doc->evaluate(
XMLString::transcode("/root/ApplicationSettings"), // <-- HERE IS THE XPATH
root,
NULL,
DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, //DOMXPathResult::ANY_UNORDERED_NODE_TYPE, //DOMXPathResult::STRING_TYPE,
NULL);
// look into the xpart evaluate result
result->snapshotItem(0);
std::cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<std::endl;;
XMLPlatformUtils::Terminate();
return 0;
}
The problem is that sometimes my XML will only have certain fields. But if I remove the ApplicationSettings entry from the XML it will seg fault. How can I properly handle these optional fields? I know that trying to correct from seg faults is risky business.
The seg fault is occurring in this line
std::cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<std::endl;
specifically in get getFirstChild() call because the result of getNodeValue() is NULL.
This is my quick and dirty solution. It's not really ideal but it works. I would prefer a more sophisticated evaluation and response.
if (result->getNodeValue() == NULL)
{
cout << "There is no result for the provided XPath " << endl;
}
else
{
cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<endl;
}

xalan and custom function for xslt

I'm using Apache FOP with the IKVM from my c# code. I generate the pdf by using the xslt stylesheet to get the result as xsl fo. I have one problem, that is usingthe custom functions.
My stylesheet declaration:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:cal="xalan://m.test"
extension-element-prefixes="cal"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/XMLSchema-instance http://www.xmlblueprint.com/documents/fop.xsd">
The custom function:
namespace m
{
public class test
{
public static string zzz(ExpressionContext x, object d)
{
return "test";
}
}
}
And calling this from the xslt:
<xsl:value-of select="cal:zzz(1)"/>
Code to compile it:
FopFactory fopFactory = FopFactory.newInstance();
fopFactory.ignoreNamespace("http://www.w3.org/2001/XMLSchema-instance");
fopFactory.setUserConfig(new File("fop.xconf"));
OutputStream o = new DotNetOutputMemoryStream();
try
{
Fop fop = fopFactory.newFop("application/pdf", o);
TransformerFactory factory = TransformerFactory.newInstance();
Source xsltSrc = new StreamSource(new File("data.xsl"));
Transformer transformer = factory.newTransformer(xsltSrc);
var bytes = System.IO.File.ReadAllBytes("data.xml"); //"HR_CV.fo");
var stream = new DotNetInputMemoryStream(new System.IO.MemoryStream(bytes));
Source src = new StreamSource(stream);
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
}
finally
{
o.close();
}
Exception I got is:
java.lang.NoSychMethodExtension: For extension function, could not find method org.apache.xml.utils.NodeVector.zzz([ExpressionContext,])
What I'm doing wrong?
You're calling the zzz function with a single argument (1). But your function expects two arguments. If you provide both arguments, chances are it will work just fine.

TinyXML2 query text if attribute matches

I am trying to figure out a way to load the text from an XML document I have created using TinyXML2. Here is the entire document.
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="15" height="13" tilewidth="32" tileheight="32">
<tileset firstgid="1" name="Background" tilewidth="32" tileheight="32">
<image source="background.png" width="64" height="32"/>
</tileset>
<tileset firstgid="3" name="Block" tilewidth="32" tileheight="32">
<image source="block.png" width="32" height="32"/>
</tileset>
<layer name="Background" width="15" height="13">
<data encoding="base64">
AgAAAAIAAAACAAAA...
</data>
</layer>
<layer name="Block" width="15" height="13">
<data encoding="base64">
AwAAAAMAAAADAAAAAwAAAAM...
</data>
</layer>
</map>
Basically, I want to copy the text from <data> into a string called background only if the layer name is "Background".
I have gotten the other variables like so:
// Get the basic information about the level
version = doc.FirstChildElement("map")->FloatAttribute("version");
orientation = doc.FirstChildElement("map")->Attribute("orientation");
mapWidth = doc.FirstChildElement("map")->IntAttribute("width");
mapHeight = doc.FirstChildElement("map")->IntAttribute("height");
That works great because I know the element name and the attribute name. Is there a way to say get the doc.FirstChildElement("map")->FirstChildElement("layer") and if it == "Background", get the text.
How would I accomplish this?
I know this thread is quite old, but just in case someone perusing the internet might stumble upon this question as I have, I wish to point out that Xanx's answer can be simplified slightly.
In tinyxml2.h it says that for the function const char* Attribute( const char* name, const char* value=0 ) const, if the value parameter is not null, then the function only returns if value and name match. According to the comments in the file this:
if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
can be written like this:
if ( ele->Attribute( "foo" ) ) {
if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
}
So the code Xanx provided can be rewritten like this:
XMLElement * node = doc.FirstChildElement("map")->FirstChildElement("layer");
std::string value;
if (node->Attribute("name", "Background")) // no need for strcmp()
{
value = node->FirtChildElement("data")->GetText();
}
A minor change, yes, but something I wanted to add.
I advice you to do something like this:
XMLElement * node = doc.FirstChildElement("map")->FirstChildElement("layer");
std::string value;
// Get the Data element's text, if its a background:
if (strcmp(node->Attribute("name"), "Background") == 0)
{
value = node->FirtChildElement("data")->GetText();
}
auto bgData = text (find_element (doc, "map/layer[#name='Background']/data"));
Using tinyxml2 extension (#include <tixml2ex.h>).
N.B. should really be wrapped in a try/catch block.
Work in progress and documentation is incomplete (can deduce from the test example until it's ready).
I'll mention in passing that the other two answers only work properly when the desired <layer> element appears first.

problem with XSLT

I am trying to create a html based template with xslt transformation.The string result returned by the transformer is perfectly fine.but when i try to send it to display, the browser is not interpreting it. The output looks like<html><body>...</body></html>.
when i do view source it is displaying <html>... How can i resolve it?Please help.
Thanks in advance
Did you specify the output method correctly? It should be set to HTML:
<xsl:output method="html" encoding="UTF-8" />
<xsl:output value="html"/>
<xsl:template match="mail[#type='pinReset']">
<html><body>
<xsl:variable name="userService" select="java:new()"/>
<i><u>Message body </u><xsl:value-of select="mailMessage/mail/body/prefix"/></i>
<a><xsl:attribute name="href">
<xsl:value-of select="java:getResetPinUrl($userService)"/></xsl:attribute>
reset pin
</a>
<i><xsl:value-of select="mailMessage/body/suffix"/></i><br/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
This is my XSl.
public String getXformedString(int type){
String xFormedString = "";
String xsltFile = "D:\\SitesContent\\sitescontent_war\\JavaSource\\com\\tgt\\mobile\\gc\\controller\\email.xsl";
String xmlFile="D:\\SitesContent\\sitescontent_war\\JavaSource\\com\\tgt\\mobile\\gc\\controller\\emailBody.xml";
StringWriter stWr = new StringWriter();
File xsltfile = new File(xsltFile);
File xmlfile = new File(xmlFile);
Source xmlSource = new StreamSource(xmlfile);
Source xsltSource = new StreamSource(xsltfile);
Result result = new StreamResult(stWr);
TransformerFactory transFact = TransformerFactory.newInstance();
try {
Transformer transformer= transFact.newTransformer(xsltSource);
transformer.setParameter("type",new Integer(type));
transformer.transform(xmlSource, result);
xFormedString = stWr.toString();
System.out.println("Str->"+xFormedString);
} catch (TransformerConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TransformerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return xFormedString;
}
This is the code to get the formed string from xml and xslt.