XML / XPath retrieve inner text of node?

Hey all,

I have the following xml schema:


<language name="Spanish" short="Es">
    <text key="Hello">
        Hola
    </text>

        <text key="House">
                Casa
        </text>
</language>

Basically theres one <language> which contains lots of <text> elements, I want to retrieve the inner text for a <text> entry with the following function:


        function getText($key)
        {
            $doc = new DOMDocument();

            $doc->load($this->_file);

            $xpath = new DOMXPath($doc);

            $nodelist = $xpath->query("language/text[@key='$key']");

            $node = $nodelist->item(0);

            // Return inner text from node.
        }

Thats what i’ve got so far, except im not sure how to retrieve the inner text for a DomNode ?

Anybody?

Thanks, Matt.

Hi…

You might just be able to do it as XPath…

string(language/text[@key='$key'])

If that doesn’t work have a look at the W3 site. They have a UML diagram somewhere showing all of the DOM methods and attributes. Probably something like getText() or somesuch. The model; should be pretty standard accross languages.

yours, Marcus

If SleapEasy isn’t too busy, you could ask for help ? Knows this stuff pretty well actually :wink:

Try this:


$nodelist->item(0)->nodeValue;

Thanks,

JT

Thanks for the responses all,

lastcraft: no such luck :frowning:

Widow Maker: Thanks but I wouldn’t want to intrude on someone that I dont know by private messaging questions to them :xeye:

seratonin: Parse Error on that line

I’m using this:


   function reduce_node_to_content($node,$attribute="content"){
   	if (is_array($node->nodeset)){
   		foreach($node->nodeset as $content){
  		 $return[]	= stripslashes($content->{$attribute});
   		}
   	}
   	else{
   		return $return[]=stripslashes($content->{$attribute});
   	}
   	return $return;
   }
   
   
   function parseXML($xml){
   	//needs Sablotron  - xPath installed
   	require_once('Pulse2/class_variable_conversion.inc.php');
   	$convert	=	&new variableConversion();
   
   	if (!empty($xml)){
   		$dom	=	domxml_open_mem($xml);
   		$calcX = 	$dom->xpath_new_context(); 
 	 $xml_parsed["notes"]	 	= $this->reduce_node_to_content($calcX->xpath_eval("//NOTES/text()"));
	}
   	return $xml_parsed;
   }
   
   

Hi PatrikG,

Thanks for the post,
Any examples of the xml schema you’re using with that?

Thanks,
Matt.

hrmpf…was a bit of a rushed post. Here goes the XML:


 <?xml version="1.0" ?>
 <!DOCTYPE SERVICE SYSTEM "Service.dtd">
 <SERVICE ID="9">
 		 <CLIENT_HIERARCHY></CLIENT_HIERARCHY>
 		 <ACCOUNT_HANDLER></ACCOUNT_HANDLER>
 		 <NOTES></NOTES>
 
 		 <ALLOCATIONS>
 					  <ALLOCATION ID="9">
 								  <TITLE></TITLE>
 								  <PHONE_NO></PHONE_NO>
 								  <DDI></DDI>
 								  <IVR_GROUP></IVR_GROUP>
 								  <DATE_START></DATE_START>
 								  <DATE_END></DATE_END>
 								  <CREATED_BY></CREATED_BY>
 								  <CREATED_DATE></CREATED_DATE>
 								  <UPDATED_BY></UPDATED_BY>
 								  <UPDATED_DATE></UPDATED_DATE>
 					  </ALLOCATION>
 		 </ALLOCATIONS>
 
 		 <SCRIPTS>
 				  <SCRIPT ID="9">
 						  <NAME></NAME>
 						  <CREATED_BY></CREATED_BY>
 						  <CREATED_DATE></CREATED_DATE>
 						  <UPDATED_BY></UPDATED_BY>
 						  <UPDATED_DATE></UPDATED_DATE>
 						  <DETAIL></DETAIL>
 				  </SCRIPT>
 		 </SCRIPTS>
 </SERVICE>
 

What Seratonin posted should work.

All DOM nodes (attributes, “tags”, text nodes, etc.) have a nodeValue property. You should check your code, and the XPath query you’re using is not properly formed (it should being with a /).

I subclassed DomXPath to add a couple of helper functions for when I want to query an XML document and either retrieve only one node, or retrieve the node’s value. This is so I don’t have to deal with the DomNodeList when I know my query is only supposed to match 1 node.

Feel free to use this if you want.


/**
 * Extension of DomXPath which adds functions for retrieveing single
 * nodes / nodevalues using XPath.
 *
 * @author		Neill Roy <>
 * @copyright	Neill Roy 2004
 * @license		GPL
 */

class Pepper_XML_XPath extends DomXPath {

	/**
	 * Constructor.
	 *
	 * @access	public
	 * @param	object DomDocument
	 * @return	void
	 */
	public function __construct(DomDocument $domDoc) {
		parent::__construct($domDoc);
	} // __construct()

	/**
	 * Loads an XML file from the given URI.
	 *
	 * @access	public
	 * @static
	 * @param	string Uri of the XML document to load
	 * @return	mixed FALSE if uri can't be loaded, Pepper_XML_XPath if it can.
	 */
	public static function loadXPathURI($uri) {
		$doc = new DomDocument();
		if (!$doc->load($uri)) {
			return FALSE;
		}
		return new Pepper_XML_XPath($doc);
	} // loadXPathURI()

	/**
	 * This is a helper method for use with XPath queries that will retrieve 1
	 * (one) matching node's value. If there is more or less that 1 matches the method
	 * will return FALSE, otherwise the single node's value will be returned.
	 *
	 * @access	public
	 * @param	string XPath query string
	 * @return	mixed
	 */
	public function getNodeValue($qry) {
		if (!$node = $this->getNode($qry)) {
			return FALSE;
		}
		return $node->nodeValue;
	} // getNodeValue()

	/**
	 * This is a helper method for use with XPath queries that will retrieve 1
	 * (one) matching node. If there is more or less that 1 matches the method
	 * will return FALSE, otherwise the single node will be returned.
	 *
	 * @access	public
	 * @param	string XPath query string
	 * @return	mixed
	 */
	public function getNode($qry) {
		// There is no way to retrieve the size of a DOMNodeList
		// so we iterate over it and if we only do one iteration return
		// the DOMNode in the DOMNodeList otherwise there are
		// not enough or to many results.
		$i = 0;
		foreach ($this->query($qry) as $result) {
			$i++;
			$last = $result;
			if ($i == 2) {
				return FALSE;
			}
		}
		if ($i == 1) {
			return $last;
		}
	} // getNode()

} // Pepper_XML_XPath

quick usage examples :


$store = Pepper_XML_XPath::loadXPathURI(self::COMMAND_MAP_URI);
$store->getNodeValue('/map/command[@id="' . $id . '"]/somenode');

This method can also be used to get the value of other node types (attributes, text)

e.g.


$xml = new DomDocument()
$xml->load('document.xml');
$store = new Pepper_XML_XPath($xml);
echo $store->getNodeValue('/map/command[@id="' . $id . '"]/somenode');
echo $store->getNodeValue('/map/command[@id="' . $id . '"]/@someattribute');
echo $store->getNodeValue('/map/command[@id="' . $id . '"]/text()');

Hope that helps.

If you need any more help or want me to explain it a bit better (I’m prone to rambling on at times) just ask.

Here is the full code listing that worked for me:


<?xml version="1.0"?>
<language name="Spanish" short="Es">
	<text key="Hello">Hola</text>
	<text key="House">Casa</text>
</language>


<?php
$doc = new DOMDocument();
$doc->load("language.xml");
$xpath = new DOMXPath($doc);
$nodelist = $xpath->query("/language/text[@key='Hello']");
echo $nodelist->item(0)->nodeValue;
?>

Please let me know if you have any more problems with it.

Thanks,

JT

Thanks very much sleapeasy :wink:

I’ll just give serantonin’s solution a go first though, but thats certainly helpful if we cannot resolve this.

The complete code I have is:


<?
	class Language
	{
		var $language = 'english';
		var $file = '../languages/english.xml';
		
		function Language()
		{
		
		}
		
		function getText($key)
		{
			$doc = new DOMDocument();
			$doc->load($this->file);
			$xpath = new DOMXPath($doc);
			$nodelist = $xpath->query("/language/text[@key='$key']");
			return $nodelist->item(0)->nodeValue; 	
		}
		
		function setLanguage($language, $file)
		{
			$this->language = $language;
			$this->file = $file;
		}
	}
?>

And calling the script:


<?
    include('classes/language.class.inc');
    $language = new Language;
			
    die($language->getText("Hello"));
?>

And its giving me a parse error on

return $nodelist->item(0)->nodeValue;

Any ideas?

Thanks
Matt.

Well I studied your code and I was pretty sure it was fine - so I tested it myself and it worked perfectly.

My setup: PHP 5RC3, Apache 2.0.48, libxml 2.5.11, Linux.

I’m stumped as to why you get a parse error on that line.

I just consulted my tech support, and they’ve told me the server has the following:

xml
XML Support active
XML Namespace Support active
EXPAT Version expat_1.95.2

domxml
DOM/XML enabled
libxml Version 2.4.19
XPath Support enabled
XPointer Support enabled

Is all that ok?

Sorry for the late reply. If you haven’t solved your problem yet:

What version of PHP are you using?

Ask your tech support if you’re not sure or use [fphp]phpversion[/fphp].

You could also try asking in the php.xml.dev newsgroup - give them all the info you gave above along with the version number and I’m sure they’ll be of more help.

Hey,

Sorry for the late reply, the project got put aside for a while and I totally forgot :xeye:

The PHP version is: 4.1.2

Matt.

Then you’ll probably need to change

return $nodelist->item(0)->nodeValue;

to

$item = $nodelist->item(0);
return $item->nodeValue;

HTH,

Azmo

That looks promising, i’ll give it a go tomorrow, thanks.

OK, now it seems to be going backward…

I have this in a language class:



function getText($key)
       		{
       			$doc = new DOMDocument();
       			
       			$doc->loadXML("<bleh></bleh>");
       			
       			//$doc->load("spanish.xml");
       			
       			$xpath = new DOMXPath($doc);
            			
           	 		$nodelist = $xpath->query("/language/text[@key='$key']");
           	 		
           	 		$item = $nodelist->item(0);
           	 		
           	 		return $item->nodeValue;
        		}


And im getting

Fatal error: Call to undefined function: loadxml() in public_html/classes/language.class.inc on line 59

same for load();

:frowning: