Getting the Text Portion of a Node Using PHP Simple Xml

Getting the text portion of a node using php Simple XML

There might be ways to achieve what you want using only SimpleXML, but in this case, the simplest way to do it is to use DOM. The good news is if you're already using SimpleXML, you don't have to change anything as DOM and SimpleXML are basically interchangeable:

// either
$articles = simplexml_load_string($xml);
echo dom_import_simplexml($articles)->textContent;

// or
$dom = new DOMDocument;
$dom->loadXML($xml);
echo $dom->documentElement->textContent;

Assuming your task is to iterate over each <article/> and get its content, your code will look like

$articles = simplexml_load_string($xml);
foreach ($articles->article as $article)
{
$articleText = dom_import_simplexml($article)->textContent;
}

get node and subnode text value in simpleXML

$book->title is of type SimpleXMLElement. You could pass that to dom_import_simplexml and get the nodeValue:

$sxml = simplexml_load_string($xml);
foreach($sxml->xpath('//book') as $book){
echo dom_import_simplexml($book->title)->nodeValue . "<br>";
// or use
// echo strip_tags($book->title->asXml()) . "<br>";
}

That will give you:

I love apple pie
I love chocolate

Demo

How to get the innerText of an element with SimpleXml

In order for the traversal

echo $xml->note->body;

To work, your markup would need to be

<note>
<note>
<body>

To avoid such errors, it is good practise to name the variable you simplexml_load_file into after the root element in the markup, e.g.

$note = simplexml_load_string($xml);

To get the "innerText" of a SimpleXmlElement you can do:

echo strip_tags($note->body->asXml());

the asXML() method will give you the "outerXML" which you then remove with strip_tags.

The alternative would be importing the node into DOM and then getting it's nodeValue

echo dom_import_simplexml($note->body)->nodeValue;

Change the value of a text node using SimpleXML

In the end I managed to do it by using the PHP XML DOM.
Here is the code that I used in order to change the text node of a specific element:

  <?php
// create new DOM document and load the data
$dom = new DOMDocument;
$dom->load('getobs.xml');
//var_dump($dom);
// Create new xpath and register the namespace
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('g','http://www.opengis.net/gml');
// query the result amd change the value to the new date
$result = $xpath->query("//g:beginPosition");
$result->item(0)->nodeValue = 'sds';
// save the values in a new xml
file_put_contents('test.xml',$dom->saveXML());
?>

SimpleXML and php - get a node that contains text that matches another node

You are most likely looking for xpath to query you document.

for example to iterate over the lines - I don't fully understand why you do it the way you do it, but here is how I understand it to go over the lines (text-only output):

$xml = simplexml_load_string($buffer);

foreach ($xml->xpath('//Details/Line') as $line)
{
printf("#%d: %s\n", $line['NUMBER'], $line->Description);
}

which gives:

#0: Account payroll deduction
#1: Mountain Mkt Sales

I guess you see how this works in general. So now for getting chit based on $line->ChitCode, which is I think actually the more interesting part for you. It also better illustrates the benefits of xpath:

foreach ($xml->xpath('//Details/Line') as $line)
{
printf("#%d: [%s] %s\n", $line['NUMBER'], $line->ChitCode, $line->Description);

$query = sprintf("//Chits/Chit[Code = '%s']/Body", $line->ChitCode);
$result = $xml->xpath($query);

if ($result) {
$chit = $result[0];
echo $chit;
} else {
echo ' -- no chit found -- ';
}
echo "\n";
}

This time the query contains the condition to look for the number (in case you want to make this save, escape the string properly (more info)), it basically creates the following two xpath queries:

//Chits/Chit[Code = 'PR DED']/Body

//Chits/Chit[Code = '03128862']/Body

I again guess you can imagine what those mean. In case such a <Body> element is found, the code above outputs it. And this is how the result looks like:

#0: [PR DED] Account payroll deduction
-- no chit found --
#1: [03128862] Mountain Mkt Sales

Member#: 0ECHER
Name: *******
Area: *********
Server: JEANNEANE
Chit#: 03128862
Date: June 5 2014
------- ------- ------- ------- -------
1 MINGUA BEEF JER 8.99
------- ------- ------- ------- -------
Sub-Total 8.99

TAX 0.61
------- ------- ------- ------- -------
Chit Total 9.60

MEMBER CHARGE -9.60

I hope this gives you enough insight to get started with it. xpath() is just a method on SimpleXMLElement so you can combine this as well with your code which I didn't copy just to have the example smaller (same reason why I use plain text output here, would work equally with HTMl but would bloat the example).


Addition by OP:

THAT WORKED GREAT!!

I adapted your code to this, and it worked perfectly:

foreach ($Linequery as $Line) {
$LineChitCode = $Line->ChitCode;
$ChitBody = sprintf("//Chits/Chit[Code = '%s']/Body", $LineChitCode);
$CBresult = $member->xpath($ChitBody);
echo "<label class='collapse' for='" . $Line->ChitCode . "-" . $Line->ChitDate . "'>" . $Line->Description . "</label>
<input id='" . $Line->ChitCode . "-" . $Line->ChitDate . "' type='checkbox'>"; // Creates hide - show link for each Description item
// Start getting the matching Chit Code from Chit/Body
if ($CBresult) {
$chit = $CBresult[0];
echo "<div>Code: " . $LineChitCode . "<br>" . $chit . "</div>";
} else {
echo "<div>" . $Line->ChitCode . "</div>";
}
}

Thank you, you saved me from 3 days of trying to figure this out!!

Get xml node full path in Php / SimpleXml

You can use the much more powerful DOM (DOMDocument based in PHP) api to do this...

$MyXml = new SimpleXMLElement($xml);
$Jhons = $MyXml->xpath('//Jhon');
foreach ($Jhons as $Jhon){
$dom = dom_import_simplexml($Jhon);
echo $dom->getNodePath().PHP_EOL;
}

The dom_import_simplexml($Jhon) converts the node and then getNodePath() displays the path...

This gives ( for the example)

/root/First/Bob/Jhon
/root/Second/Mary/Jhon

Or if you just want to stick to SimpleXML, you can use the XPath axes ancestor-or-self to list the current node and each parent node...

$MyXml = new SimpleXMLElement($xml);
$Jhons = $MyXml->xpath('//Jhon');
foreach ($Jhons as $Jhon){
$parent = $Jhon->xpath("ancestor-or-self::*");
foreach ( $parent as $p ) {
echo "/".$p->getName();
}
echo PHP_EOL;
}

SimpleXML get Element Content between Child Elements

As others have said, SimpleXML doesn't have any support for accessing individual text nodes as separate entities, so you will need to supplement it with some DOM methods. Thankfully, you can switch between the two at will using dom_import_simplexml and simplexml_import_dom.

The key pieces of DOM functionality you need are:

  • the DOMElement->childNodes member variable for accessing all nodes directly under a particular element as an iterable list
  • the DOMNode->nodeType variable for determining if a particular child is a text node or an element
  • the DOMNode->nodeValue variable to get the actual text

Given those, you can write a function which returns an array with a mixture of SimpleXML objects for child elements, and strings for child text nodes, something like this:

function get_child_elements_and_text_nodes($sx_element)
{
$return = array();

$dom_element = dom_import_simplexml($sx_element);
foreach ( $dom_element->childNodes as $dom_child )
{
switch ( $dom_child->nodeType )
{
case XML_TEXT_NODE:
$return[] = $dom_child->nodeValue;
break;
case XML_ELEMENT_NODE:
$return[] = simplexml_import_dom($dom_child);
break;
}
}

return $return;
}

In your case, you need to recurse down the tree, which makes it a little confusing if you mix DOM and SimpleXML as you go, so you could instead write the recursion entirely in DOM and convert the SimpleXML object before running it:

function recursively_find_text_nodes($dom_element)
{
$return = array();

foreach ( $dom_element->childNodes as $dom_child )
{
switch ( $dom_child->nodeType )
{
case XML_TEXT_NODE:
$return[] = $dom_child->nodeValue;
break;
case XML_ELEMENT_NODE:
$return = array_merge($return, recursively_find_text_nodes($dom_child));
break;
}
}

return $return;
}

$text_nodes = recursively_find_text_nodes(dom_import_simplexml($simplexml->element));

Here's a live demo of that last function.

SimpleXML get node value

$xml = new SimpleXMLElement($xml);
$this->xmlcode = (string) $xml->parent[0]->child1;

php/simplexml adding elements before and after text

From the viewpoint of DOM (and SimpleXML is an abstraction on top of that), you're do not insert elements around text. You replace a text nodes with a mix of text nodes and element nodes. SimpleXML has some problems with mixed child nodes, so you might want to use DOM directly. Here is a commented example:

$xml = <<<'XML'
<record>
<letter>
<header>To Alice from Bob</header>
<body>Hi, how is it going?</body>
</letter>
</record>
XML;

// the words and the tags you would like to create
$words = ['Alice' => 'to', 'Bob' => 'from'];
// a split pattern, you could built this from the array
$pattern = '((Alice|Bob))';

// bootstrap the DOM
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

// iterate any text node with content
foreach ($xpath->evaluate('//text()[normalize-space() != ""]') as $text) {
// use the pattern to split the text into an list
$parts = preg_split($pattern, $text->textContent, -1, PREG_SPLIT_DELIM_CAPTURE);
// if it was split actually
if (count($parts) > 1) {
/// iterate the text parts
foreach ($parts as $part) {
// if it is a word from the list
if (isset($words[$part])) {
// add the new element node
$wrap = $text->parentNode->insertBefore(
$document->createElement($words[$part]),
$text
);
// and add the text as a child node to it
$wrap->appendChild($document->createTextNode($part));
} else {
// otherwise add the text as a new text node
$text->parentNode->insertBefore(
$document->createTextNode($part),
$text
);
}
}
// remove the original text node
$text->parentNode->removeChild($text);
}
}

echo $document->saveXml();

Output:

<?xml version="1.0"?>
<record>
<letter>
<header>To <to>Alice</to> from <from>Bob</from></header>
<body>Hi, how is it going?</body>
</letter>
</record>


Related Topics



Leave a reply



Submit