How can print node value XML in an orderly way PHP - php

I have a XML like this:
<description>
<heading id="01"> Math </heading>
<p id="01"> Text 1 </p>
<heading id="02"> History </heading>
<p id="02"> Text 2</p>
<p id="03"> Text 3</p>
<heading id="03"> Biology </heading>
<p id="04"> Text 4 </p>
</description>
I also have many xml files have structure like this one, they are only different from amount of <p> node of every <heading> node.
How can I print <heading> and some <p> node and the second heading....
I tried to use foreach, but it's not true.
my code:
<?php
$xml=simplexml_load_file("NWB2.xml") or die("Error: Cannot create object");
echo "<b>".$xml->{'description'}->{'heading'}."</b>";
echo "<p>".$xml->{'description'}->{'p'}."</p>";
?>

If it's just a case of having different style depending on the input tag, you can use a foreach() loop and output the value according to the tag type...
$xml=simplexml_load_file("NWB2.xml") or die("Error: Cannot create object");
foreach ( $xml as $type => $value ) {
if ($type == "p") {
echo $value;
}
else {
echo "<b>$value</b>";
}
}

Related

Using xpath to query XML in PHP

I am using xpath for the first time to query an xml document for a value and return the corresponding name value. I have been reading numerous different sources on the topic but I can't seem to get it to output correctly. Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<books>
<book>
<name>Genesis</name>
<abrv>GEN</abrv>
<title>The First Book of Moses</title>
</book>
<book>
<name>Exodus</name>
<abrv>EXO</abrv>
<title>The Second Book of Moses</title>
</book>
<book>
<name>Leviticus</name>
<abrv>LEV</abrv>
<title>The Third Book of Moses</title>
</book>
</books>
Here is my PHP/HTML:
<?php $root = $_SERVER['DOCUMENT_ROOT'] . "/";
$bookID = 'GEN';
$xml = simplexml_load_file( $root . 'assets/xml/books.xml' ) or die( "No Page Data Found" );
$bookName = $xml->xpath( '//book[abrv="'.$bookID.'"]/name' );
?>
<article id="verseOfDay">
<div class="container">
<h2>verse of the day</h2>
<h6><?php echo $bookName; ?></h6> // Line 19
<p></p>
</div>
</article>
Finally, here is the notice that I am receiving:
NOTICE: ARRAY TO STRING CONVERSION IN /HOME/#####/MYWEBSITE.COM/ASSETS/INCLUDES/VERSEOFTHEDAY.PHP ON LINE 19
ARRAY
What am I doing wrong? In this example I am expecting it to output Genesis in the tag.
The SimpleXML xpath function returns an array of results, which will be empty if no result is found. So you need to check for that, and if you get a result, it will be at $bookName[0]. Something like this will work:
$bookNames = $xml->xpath( '//book[abrv="'.$bookID.'"]/name' );
$bookName = empty($bookNames) ? 'Not Found' : $bookNames[0];
?>
<article id="verseOfDay">
<div class="container">
<h2>verse of the day</h2>
<h6><?php echo $bookName; ?></h6>
<p></p>
</div>
</article>
Demo on 3v4l.org

How to read all nodes of a XML file with more than three nodes levels using PHP?

I am currently writing a PHP script to read all nodes of a XML file with more than three node levels (depth > 2). However I only could read accurately upto first level child.
I would highly appreciate, if you could let me know the error I have made while I am trying to read second level child nodes.
My xml file
<?xml version="1.0" encoding="utf-8"?>
<document nipperstudio="2.3.10.3500" xmlversion="2" xmlrevision="3">
<report>
<part index="1" title="Your Report" ref="YOURREPORT">
<section index="1.1" title="Introduction" ref="INTRODUCTION">
<text>Inside the section 1.1.:</text>
<list type="bullet">
<listitem>detailed description of list item 01;</listitem>
<listitem>detailed description of list item 02;</listitem>
</list>
</section>
<section index="1.2" title="Report Conventions" ref="REPORTCONVENTIONS">
<text>This report makes use of the text conventions.</text>
<table index="3" title="Report text conventions" ref="REPORTTEXTCONVENTIONS">
<headings>
<heading>Convention</heading>
<heading>Description</heading>
</headings>
</table>
</section>
</part>
<part index="2" title="Security Audit" ref="SECURITYAUDIT">
<section index="2.1" title="Introduction" ref="INTRODUCTION">
<text>Inside the section 2.1.:</text>
<list type="bullet">
<listitem>detailed description of list item 01;</listitem>
<listitem>detailed description of list item 02;</listitem>
</list>
<section index="2.1.1" title="Issue Overview" ref="ISSUEOVERVIEW">
<text>Inside the section 2.1.1</text>
<text title="Issue Finding">The is the body text of 2.1.1.</text>
</section>
<section index="2.1.2" title="Rating Overview" ref="RATINGSYSTEM">
<text>Inside the section 2.1.1</text>
<text title="Issue Finding">The is the body text of 2.1.1.</text>
</section>
</section>
<section index="2.2" title="section title" ref="SECTION2.2">
<section index="2.2.1" title="Finding" ref="FINDING">
<text>Inside the section 2.2.1</text>
<text title="Issue Finding">The is the body text of 2.2.1.</text>
</section>
</section>
</part>
</report>
</document>
My PHP Script is given below.
Test XML Reader
<html>
<title>Test XML Reader</title>
<body>
<p>Output from xmlreader</p>
<?php
readXmlFiles();
?>
</body>
</html>
<?php
function readXmlFiles(){
// create the reader object
$reader = new XMLReader();
// reader the XML file.
$reader->open("./fwxml/03.xml"); //open the xml file to read.
while($reader->read()) {
switch($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName == 'report') { //read the local name of the node
$node = $reader->expand();
$dom = new DomDocument();
$n = $dom->importNode($node,true);
$dom->appendChild($n);
foreach(($dom->getElementsByTagName('part')) as $fwpart) {
$parttitle = $fwpart->getAttribute('title');
echo "=====".$parttitle."=====<br>";
foreach(($fwpart->childNodes) as $cnode){
if($cnode->nodeName == 'section'){
$index = $cnode->getAttribute('index');
$title = $cnode->getAttribute('title');
$ref = $cnode->getAttribute('ref');
echo "Index = " .$index."<br>";
echo "Title = " .$title."<br>";
echo "Ref = " .$ref."<br>";
$fwsec = $dom->getElementsByTagName('section');
echo $fwsec->item(0)->nodeValue."<br>";
echo "<br><br><br>";
}//end of if
}//end of foreach
}
}
break; //end of XMLREADER::ELEMENT
case (XMLREADER::END_ELEMENT):
// do something based on when the element closes.
break;
}
}
} //end of function
?>

php - Simple HTML dom - elements between other elements

I'm trying to write a php script to crawl a website and keep some elements in data base.
Here is my problem : A web page is written like this :
<h2>The title 1</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>
<h2>The title 2</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>
<p class="one_class"> Some different text </p>
<p> Some other interesting text </p>
<h2>The title 3</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>
I want to get only the h2 and p with interesting text, not the p class="one_class".
I tried this php code :
<?php
$numberP = 0;
foreach($html->find('p') as $p)
{
$pIsOneClass = PIsOneClass($html, $p);
if($pIsOneClass == false)
{
echo $p->outertext;
$h2 = $html->find("h2", $numberP);
echo $h2->outertext;
$numberP++;
}
}
?>
the function PIsOneClass($html, $p) is :
<?php
function PIsOneClass($html, $p)
{
foreach($html->find("p.one_class") as $p_one_class)
{
if($p == $p_one_class)
{
return true;
}
}
return false;
}
?>
It doesn't work, i understand why but i don't know how to resolve it.
How can we say "I want every p without class who are between two h2 ?"
Thx a lot !
This task is easier with XPath, since you're scraping more than one element and you want to keep the source in order. You can use PHP's DOM library, which includes DOMXPath, to find and filter the elements you want:
$html = '<h2>The title 1</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>
<h2>The title 2</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>
<p class="one_class"> Some different text </p>
<p> Some other interesting text </p>
<h2>The title 3</h2>
<p class="one_class"> Some text </p>
<p> Some interesting text </p>';
# create a new DOM document and load the html
$dom = new DOMDocument;
$dom->loadHTML($html);
# create a new DOMXPath object
$xp = new DOMXPath($dom);
# search for all h2 elements and all p elements that do not have the class 'one_class'
$interest = $xp->query('//h2 | //p[not(#class="one_class")]');
# iterate through the array of search results (h2 and p elements), printing out node
# names and values
foreach ($interest as $i) {
echo "node " . $i->nodeName . ", value: " . $i->nodeValue . PHP_EOL;
}
Output:
node h2, value: The title 1
node p, value: Some interesting text
node h2, value: The title 2
node p, value: Some interesting text
node p, value: Some other interesting text
node h2, value: The title 3
node p, value: Some interesting text
As you can see, the source text stays in order, and it's easy to eliminate the nodes you don't want.
From the simpleHTML dom manual
[attribute=value]
Matches elements that have the specified attribute with a certain value.
or
[!attribute]
Matches elements that don't have the specified attribute.

Transform complex and variable xml

I've a complex XML that I want to transform in HTML. Some tags need to be replaced in html tags.
The XML is this:
<root>
<div>
<p>
<em>bol text</em>, some normale text
</p>
</div>
<list>
<listitem>
normal text inside list <em>bold inside list</em>
</listitem>
<listitem>
another text in list...
</listitem>
</list>
<p>
A sample paragraph
</p>
The text inside the element is variable, which means that the other xml that I parse can completely change.
The output I want is this (for this scenario):
<root>
<div>
<p>
<strong>bol text</strong>, some normale text
</p>
</div>
<ul>
<li>
normal text inside list <strong>bold inside list</strong>
</li>
<li>
another text in list...
</li>
</ul>
<p>
A sample paragraph
</p>
</root>
I make a recursive function for parse any single node of xml and replace it in HTML tag (but doesn't work):
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->load('section.xml');
echo $doc->saveHTML();
function printHtml(DOMNode $node)
{
if ($node->hasChildNodes())
{
foreach ($node->childNodes as $child)
{
printHtml($child);
}
}
if ($node->nodeName == 'em')
{
$newNode = $node->ownerDocument->createElement('strong', $node->nodeValue);
$node->parentNode->replaceChild($newNode, $node);
}
if ($node->nodeName == 'listitem')
{
$newNode = $node->ownerDocument->createElement('li', $node->nodeValue);
$node->parentNode->replaceChild($newNode, $node);
}
}
Can anyone help me?
This is an example of a complete xml:
<root>
<div>
<p>
<em>bol text</em>, some normale text
</p>
</div>
<list>
<listitem>
normal text inside list <em>bold inside list</em>
</listitem>
<listitem>
another text in list...
</listitem>
</list>
<media>
<info isVisible="false">
<title>
<p>Image title <em>in bold</em> not in bold</p>
</title>
</info>
<file isVisible="true">
<href>
"path/to/file.jpg"
</href>
</file>
</media>
<p>
A sample paragraph
</p>
</root>
Which has to be transformed into:
<root>
<div>
<p>
<strong>bol text</strong>, some normale text
</p>
</div>
<ul>
<li>
normal text inside list <em>bold inside list</em>
</li>
<li>
another text in list...
</li>
</ul>
<!-- the media tag can be presented in two mode: with title visible, and title hidden -->
<!-- this is the case when the title is hidden -->
<img src="path/to/file.jpg" />
<!-- this is the case when the title is visible -->
<!-- the info tag (inside media tag) has an attribute isVisible="false" which means it doesn't have to be shown. -->
<!-- if the info tag has visible=true, the media tag must be translated into
<div>
<img src="path/to/file.jpg" />
<p>Image title <strong>in bold</strong> not in bold</p>
<div>
-->
<p>
A sample paragraph
</p>
</root>
There's a language specially designed for this task: it's called XSLT, and you can easily express your desired transformation in XSLT and invoke it from your PHP program. There's a learning curve, of course, but it's a much better solution than writing low-level DOM code.
In XSLT you write a set of template rules saying how individual elements should be handled. Many elements in your example are copied through unchanged, so you can start with a default rule that does this:
<xsl:template match="*">
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>
The "match" part says what part of the input you are matching; the body of the rule says what output to produce. The xsl:apply-templates does a recursive descent to process the children of the current element.
Some of your elements are simply renamed, for example
<xsl:template match="listitem">
<li><xsl:apply-templates/></li>
</xsl:template>
Some of the rules are a little bit more complex, but still easily expressed:
<xsl:tempate match="media/file[#isVisible='true']">
<img src="{href}"/>
</xsl:template>
I hope you agree that this declarative rule-based approach is much clearer than your procedural code; it's also much easier for someone else to change the rules in six months' time.
Well, maybe, it's not the most correct idea, but why not just to use str_replace? That way You will see clearly the list of changes to apply and add / remove new ones easily.
file_get_contents $file = file_get_contents('file.xml');
str_replace $file = str_replace("<em>", "<strong>", $file);
file_put_contents file_put_contents('file.html', $file);
UPDATE (Some more ideas regarding the changes in the question)
This seems a little bit tricky (at least for me now) to use PHP + DOM here. Maybe, it would be more reasonable to use XSL / XSLT (Extensible Stylesheet Language Transformations). In that case, smth. similar can be found here: How to replace a node-name with another in Xslt?
XSLT specifically used for Language Transformations http://en.wikipedia.org/wiki/XSLT

Modify value of an XML element in foreach loop using simpleXML in php

I want to modify XML values (of the file) using simpleXML in a foreach loop.
My XML look like that :
<a>
<b id="first">
<c></c>
<d></d>
</b>
<b id="second">
<c></c>
<d></d>
</b>
<b id="third">
<c></c>
<d></d>
</b>
And my php look like that :
$xml = simplexml_load_file('myfile.xml');
$idOfB = 'first'; // the id of b and I want to modify the children nodes of that
foreach($xml->a->b as $node) {
foreach($node->attributes() as $id => $value) {
if((string)$value == $idOfB) {
// Something there I guess...
// I want to change the value of c and d of this node
}
}
}
$xml->asXML();
I have tried and searched quite a lot without any luck.
Your testing file suppose to be like ---
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b id="first">
<c>Abhishek</c>
<d/>
</b>
<b id="second">
<c/>
<d/>
</b>
<b id="third">
<c/>
<d/>
</b>
</a>
here abhishek will be replaced by Nishant
<?php
$xml = simplexml_load_file("testingxml.xml");
$idOfB = 'first'; // the id of b and I want to modify the children nodes of that
foreach($xml->b as $node) {
foreach($node->attributes() as $id => $value) {
if((string)$value == $idOfB) {
$node->c = 'Nishant';
$node->d = 'Tyagi';
// Something there I guess...
// I want to change the value of c and d of this node
}
}
//$finobjArr[] = $node;
}
//$xmlobj = $finobjArr;
file_put_contents("testingxml.xml", $xml->saveXML());
////$xml->asXML("testingxml.xml");
//print($xml->asXML("testingxml.xml"));
?>

Categories