|
xpath_eval
Evaluates the XPath Location Path in the given string
(PHP 4 >= 4.0.4)
Code Examples / Notes » xpath_evaltk dot lists
You can indeed use the result object of xpath_eval(). You just have to be careful to pass the result by reference! (note the ampersand's position). $objXP = xpath_new_context($objDom) $objTest = &xpath_eval($objXP,"//lalala"); $objTest->nodeset[0]->set_attribute("test","test data"); echo htlentities($objDom->dump_mem()); just be careful that is you pass around values from $objTest then they also need to be passed by reference. newsforsam
xpath_eval() returns only a copy of your document. So you cant for example change the $foo->nodeset[id]->content's of the resulting matches. If you want to, you have to do it yourself by going recursive through your doc, which makes xpath_eval at least useless except for checking if you have to ;).
pking
This is a very (very) minor point, but there is a comma missing in the function definition for xpath_eval. This being my first experience with xpath, I thought "object xpath context" was refering to a single parameter produced by a previous call to xpath_new_context(). Then I couldn't see where you would add the query (which is actually the context parameter) So the proper definition should be array xpath_eval (object xpath, context) Additionally an example would be nice. I found one from a post to phpbuilder.com: ------------------------------- http://www.phpbuilder.com/annotate/message.php3?id=1002772 ------------------------------- Message # 1002772: Date: 01/02/01 06:40 By: Luis Argerich Subject: new DOM features im 4.0.4 Just wanted to add that PHP 4.0.4 has improved DOM support including Xpath and Xpointer support: Try this: $xml='SOME XML ....'; $doc=xmldoc($xml); $ctx=xpath_new_context($doc); $foo=xpath_eval($ctx,"//title"); print_r($foo); It returns an object that contains a property called Nodeset with an array of DomNodes with the result of the Xpath expression. print_r($foo) to see the full structure. 4.0.4 has also added Xpointer support, so with Xpath and Xpointer support we can really do a lot of things from PHP to XML files. Luis. sbarnum@pointsystems com
This function has come in handy for recursively viewing the results of xpath searches. It iterates through a node and converts it to a big associative array: /** * Recursive function to convert xml root node to big assoc array */ function xmlnode2array($node) { if ($node->type==XML_ELEMENT_NODE) { if ($attrArray = $node->attributes()) { // parse attributes // foreach($attrArray AS $attr) { $out['ATTRIBUTE'][$attr->name] = $attr->value; } } if ($childArray = $node->children()) { // add child nodes // foreach($childArray AS $child) { if ($child->type==XML_ELEMENT_NODE) { $out[$child->tagname][] = xmlnode2array($child); } else { if ($content = xmlnode2array($child)) $out['CONTENT'] = $content; } } } } else { // this is a CONTENT NODE // $out = trim($node->content); if (!$out) return false; } return $out; } fabiostt x_at_x libero x_dot_x .it
Querying documents closed inside a namespace can be tricky http://bugs.php.net/bug.php?id=11903 tuxo
PHP Version: 4.3.1 I tried out how to get a part of a xml document with the xpath functions in domxml. Try the following solution: <?php // get dom object $xmldoc = domxml_open_mem($xml); // init xpath $xpath = xpath_new_context($xmldoc); $xpresult = xpath_eval($xpath, "/root/info"); // dump all nodes directly in plain text foreach ($xpresult->nodeset as $node) { $newxml .= $node->dump_node($node); } ?> If you wanna get a new dom object of the result just add $newxmldoc = domxml_open_mem($newxml); patrikg
Just an example of how to grab XML attributes with xpath - which took me a while to figure out. I'm filtering the returned object function node_content() which is a somewhat quick'n dirty solution, but I don't always need XML's child-parent relationships. <?php $xml='<MY_SERVICE> <MERCHANDISE> <SERVICE TYPE="books"> <NAME>Ulysses</NAME> </SERVICE> <SERVICE TYPE="books"> <NAME>The Poisonwood Bible</NAME> </SERVICE> <SERVICE TYPE="cars"> <NAME>Van</NAME> </SERVICE> <SERVICE TYPE="vehicle sans wheels"> <NAME>UFO</NAME> </SERVICE> </MERCHANDISE> </MY_SERVICE>'; echo "<h4>XML</h4><xmp>";print_r(parse_XML($xml));echo"</xmp>"; function node_content($node,$attribute="content"){ foreach($node->nodeset as $content){ $return[] = $content->{$attribute}; } return $return; } function parse_XML($xml){ //needs PHP's xPath extension installed $dom =domxml_open_mem($xml); $calcX = &$dom->xpath_new_context(); $xml_parsed["merchandise"]=node_content( $calcX->xpath_eval("//MERCHANDISE/SERVICE/NAME/text()") ); $xml_parsed["service"]=node_content( $calcX->xpath_eval("//MERCHANDISE/SERVICE/attribute::TYPE",$calcX) ,"value"); return $xml_parsed; } ?> The code above returns: XML Array ( [merchandise] => Array ( [0] => Ulysses [1] => The Poisonwood Bible [2] => Van [3] => UFO ) [service] => Array ( [0] => books [1] => books [2] => cars [3] => vehicle sans wheels ) ) ziw
it seems that namespaces are not yet (PHP 4.06) implemented - xpath_eval($cnx,"/ns:tag") does work on w2k and does NOT on linux
brandon dot whitehead
In order to use the default namespace you must understand how namespace prefixes work. Prefixes are simply convenient mappies to the namespace URI. For example, if you set the namespace: xmlns:xm="http://www.someurl.org" and you have the following document fragment: <rootnode><xm:childnode>Text</xm:childnode></rootnode> this is essentially equivalent to: <rootnode> <http://www.someurl.org:childnode> Text </http://www.someurl.org:childnode> </rootnode> because the namespace URI is what matters, not the namespace prefix. Unfortuantly, if you have a default namespace: xmlns="http://www.anotherurl.org" then all elements without a prefix belong to that namespace, and yet, it appears that PHP, and the underlying LIBXML2 don't let you register a default namespace with "xpath_register_ns(context, prefix, uri)" i.e. by leaving the prefix = "". Therefore, to get around the problem, simply give the default prefix a simple name, such as "pre". For example, if you have a default namespace declaration such as the following document: <?xml version="1.0" encoding="UTF-8"?> <rootname xmlns="http://www.some.org" xml:lang="en-US"> <childnode>Some text</childnode> </rootname> And you want to evaluate the xpath expression: "/rootname/childnode" then you need to register the default namespace in PHP like this: xpath_register_ns(context, "pre", "http://www.some.org"); and then use the following xpath expression: "/pre:rootname/pre:childnode" As you can see this is a lot prettier and more intuititive than using the local-name() function. In addition, it makes your code more portable, because you are guaranteed to always be working on nodes that belong to your explicitly stated namespace, uniquely identified by your URI. arthur
If you want to get the XPath for a particular node: function getXPath($node) { /* node id is held in a property named '1', this is illegal in php so we use a workaround */ $one = '1'; $xpath = ''; while ($parent = $node->parent_node()) { $siblings = $parent->child_nodes(); $index = 1; foreach ($siblings as $sibling) { if ($sibling->type != XML_ELEMENT_NODE || $sibling->tagname != $node->tagname) continue; if ($sibling->$one == $node->$one) { $xpath = '/' . $node->tagname . '[' . $index . ']' . $xpath; break; } $index++; } $node = $parent; } return $xpath; } chregu
If you want to apply an XPath-Expression to a particular node: $ctx->xpath_eval("xpath",$node); marius kreis email to mariuskreis . de
If the namespace is subject to change you can write even more portable code if you extend brandon dot whitehead at orst dot edu's solution like this: $doc = domxml_open_mem($xml); $xpath = $doc->xpath_new_context(); $namespace = $xpath->xpath_eval('namespace-uri(//*)')->value; // returns the namespace uri xpath_register_ns($xpath, "pre", $namespace); // sets the prefix "pre" for the namespace $obj = $xpath->xpath_eval('//pre:Offer'); // finds all Offer tags $nodeset = $obj->nodeset; print_r($nodeset); This code will determine the namespace of the root element and set a prefix for XPath queries. Thus it doesn't matter if the namespace is changing in your XML (like some webservices do...) sofnology
I hope this little example helps someone out. If the XML data doesn't come thru in the post feel free to contact me via email. <? $p = xslt_create(); $o += 0; $s = ''; $s .= "<query type='create'>"; $s .= "<resourceClass id='12345678901234567890' displayName='DAISY'>"; $s .= "<group family='global' id='kind'>"; $s .= "<node id='NODE_A' displayName='Red Ferrari' description='Red always goes faster'/>"; $s .= "</group>"; $s .= "</resourceClass>"; $s .= "<resourceClass id='12345678901234567890' displayName='BETTY'>"; $s .= "<group family='global' id='kind'>"; $s .= "<node id='NODE_B' displayName='Blue Porsche' description='But Porsches are a drivers car'/>"; $s .= "</group>"; $s .= "</resourceClass>"; $s .= "</query>"; $dom=xmldoc($s); $ctx=xpath_new_context($dom); $query_xo = xpath_eval($ctx,"count(/query/resourceClass)"); $num_rc = $query_xo->value; echo(" There are $num_rc classes in this list"); for($x=1; $x <= $num_rc; $x++){ $query_xo = xpath_eval($ctx,"/query/resourceClass[position()=$x]"); $query_ns = $query_xo->nodeset; $resourceClass_dn = $query_ns[0]; // echo("<PRE>"); // print_r( $query_xo ); // echo("<PRE><HR>"); // print_r( $query_ns ); // echo("<PRE><HR>"); // print_r( $rc_dn ); echo(" [id::".$resourceClass_dn->get_attribute('id')."][displayName::".$resourceClass_dn->get_attribute('displayName')."]"); } ?> 28-mar-2001 03:12
actually the description at the top should read: object xpath_eval(object context, string xpath); bate
<? $xml = xmldocfile('file.xml'); $xpath = $xml->xpath_new_context(); /** * object access */ $ret = $xpath->xpath_eval('//tag'); /** * function access */ $ret2 = xpath_eval($xpath, '//tag'); print_r($ret); print_r($ret2); ?> mfkahn2_nospam
$ctx = xpath_new_context($doc); $xpath_nodes = xpath_eval($ctx, "//some_element"); $xpath_nodes->nodeset[i]->set_content($string) allows you to set the node content. Try it and then do a $doc->dumpmem, you'll see the nodes in the original document are indeed updated properly. I've used this feature lots. It does work. |
Change LanguageDomAttribute->name DomAttribute->set_value DomAttribute->specified DomAttribute->value DomDocument->add_root DomDocument->create_attribute DomDocument->create_cdata_section DomDocument->create_comment DomDocument->create_element_ns DomDocument->create_element DomDocument->create_entity_reference DomDocument->create_processing_instruction DomDocument->create_text_node DomDocument->doctype DomDocument->document_element DomDocument->dump_file DomDocument->dump_mem DomDocument->get_element_by_id DomDocument->get_elements_by_tagname DomDocument->html_dump_mem DomDocument->xinclude DomDocumentType->entities() DomDocumentType->internal_subset() DomDocumentType->name() DomDocumentType->notations() DomDocumentType->public_id() DomDocumentType->system_id() DomElement->get_attribute_node() DomElement->get_attribute() DomElement->get_elements_by_tagname() DomElement->has_attribute() DomElement->remove_attribute() DomElement->set_attribute_node() DomElement->set_attribute() DomElement->tagname() DomNode->add_namespace DomNode->append_child DomNode->append_sibling DomNode->attributes DomNode->child_nodes DomNode->clone_node DomNode->dump_node DomNode->first_child DomNode->get_content DomNode->has_attributes DomNode->has_child_nodes DomNode->insert_before DomNode->is_blank_node DomNode->last_child DomNode->next_sibling DomNode->node_name DomNode->node_type DomNode->node_value DomNode->owner_document DomNode->parent_node DomNode->prefix DomNode->previous_sibling DomNode->remove_child DomNode->replace_child DomNode->replace_node DomNode->set_content DomNode->set_name DomNode->set_namespace DomNode->unlink_node DomProcessingInstruction->data DomProcessingInstruction->target DomXsltStylesheet->process() DomXsltStylesheet->result_dump_file() DomXsltStylesheet->result_dump_mem() domxml_new_doc domxml_open_file domxml_open_mem domxml_version domxml_xmltree domxml_xslt_stylesheet_doc domxml_xslt_stylesheet_file domxml_xslt_stylesheet domxml_xslt_version xpath_eval_expression xpath_eval xpath_new_context xpath_register_ns_auto xpath_register_ns xptr_eval xptr_new_context |