Reputation: 541
source.xml
<property>
<name>a</name>
<description>aaa</description>
<example value="b" description="bbb">b1</example>
<example value="c" description="ccc">c1</example>
</property>
search.php
$xmlDoc=new DOMDocument();
$xmlDoc->load("source.xml");
$x=$xmlDoc->getElementsByTagName('property');
$z=$x->item(0);
$c=$z->childNodes;
for ($j=4;$j<($cd->length);$j++) {
echo ("<div>" . $c->item($j)->attributes()->description . "</div>");
echo ("<div>" . $c->item($j)->childNodes["description"] . "</div>");
echo ("<div>" . $c->item($j)["description"] . "</div>");
}
I want to return an attribute description
. I have tried a lot, but nothing working correctly.
Upvotes: 0
Views: 1476
Reputation: 48649
<?php
$xmlDoc=new DOMDocument();
$xmlDoc->load('source.xml');
$properties = $xmlDoc->getElementsByTagName('property');
$first_property = $properties->item(0);
echo "$first_property->tagName\n";
$first_property_children = $first_property->childNodes;
echo $first_property_children->length;
/*
for ($j=4;$j<($cd->length);$j++) {
echo ("<div>" . $c->item($j)->attributes()->description . "</div>");
echo ("<div>" . $c->item($j)->childNodes["description"] . "</div>");
echo ("<div>" . $c->item($j)["description"] . "</div>");
}
*/
?>
--output:--
property
9
9??!! Wtf?! Here's what your xml file really looks like:
<property>\n #<==SEE THIS??
<name>a</name>\n #<==AND HERE??
<description>aaa</description>\n
<example value="b" description="bbb">b1</example>\n
<example value="c" description="ccc">c1</example> \n
</property>
When you hit Return
in a text file, an invisible newline character is entered into the file. Every one of the newlines is considered by xml to be enclosed in an invisible text node. Because there are 5 text nodes created by the 5 newlines inside the property
tag, as well as the 4 other visible nodes, there are a total of 9 children in the property
tag.
In your loop, so as to only land on the visible elements, you could start at childNodes[1]
(skipping the first newline text node), and then skip every other childNode:
for ($j=1; $j<($property_children->length); $j += 2) {
echo ("<div>" . $property_children->item($j)->nodeValue . "</div>\n");
//echo ("<div>" . $first_property_children->item($j)->nodeValue . "</div>");
//echo ("<div>" . $first_property_children->item($j)->childNodes["description"] . "</div>");
//echo ("<div>" . $first_property_children->item($j)["description"] . "</div>");
}
?>
--output:--
<div>a</div>
<div>aaa</div>
<div>b1</div>
<div>c1</div>
That gives you the text of all the visible tags.
Instead of looping, you can figure out the position of the description tag in the childNodes array. Here is where your description tag is located in the childNodes array:
+- from the spaces at the start of the next line
|
0 V 1 2 3
<property><text>\n </text><name>a</name><text>\n </text><description>aaa</description>....
So, you can write:
echo $property_children->item(3)->nodeValue; //nodeValue gives you the text
--output:--
aaa
But having to count nodes and take into account invisible text nodes created by whitespace inside a tag is too much of a hassle for us poor programmers, so there are other ways of obtaining a child node:
<?php
if ($property = simplexml_load_file('source.xml') )
{
//Grab all the child description tags in an Array:
echo $property->description[0] . "\n";
//Doing the same thing with xpath(which treats xml like a directory structure on your computer):
echo $property->xpath('./description')[0] . "\n";
}
?>
--output:--
aaa
aaa
See Basic SimpleXML usage in the php docs.
I want to return an attribute description.
An attribute? You mean the description attributes for all the example tags?
You can treat a tag like it is an array and use the attribute name as the subscript:
<?php
if ($property = simplexml_load_file('source.xml') )
{
$example_tags = $property->example;
foreach($example_tags as $example_tag) {
echo $example_tag['description'];
}
}
?>
--output:--
bbb
ccc
Here it is with xpath:
<?php
if ($property = simplexml_load_file('source.xml') )
{
$description_attrs = $property->xpath('./example/@description');
foreach($description_attrs as $description_attr) {
echo "$description_attr\n";
}
}
?>
--output:--
bbb
ccc
Upvotes: 0
Reputation: 19512
Anything in a XML document, will result in a node in the DOM, elements, attributes, namespace definitions, the xml declaration text and even whitespaces. Whitespaces like line breaks and spaces are stored as text nodes.
You can choose to ignore the whitespace nodes:
$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = FALSE;
$xmlDoc->load($xmlFile);
...
PHPs DOM extension has support for XPath. With it you can use expressions to fetch nodes and values.
$source = new DOMDocument();
$source->loadXml($xml);
$xpath = new DOMXPath($source);
$target = new DOMDocument();
$fragment = $target->createDocumentFragment();
foreach ($xpath->evaluate('//property') as $property) {
$fragment
->appendChild($target->createElement('div'))
->appendChild(
$target->createTextNode(
$xpath->evaluate('string(name)', $property)
)
);
$fragment
->appendChild($target->createElement('div'))
->appendChild(
$target->createTextNode(
$xpath->evaluate('string(description)', $property)
)
);
foreach ($xpath->evaluate('example', $property) as $example) {
$fragment
->appendChild($target->createElement('div'))
->appendChild(
$target->createTextNode(
$xpath->evaluate('string(@description)', $example)
)
);
}
}
echo $target->saveHtml($fragment);
Output:
<div>a</div><div>aaa</div><div>bbb</div><div>ccc</div>
Using DOM to generate the HTML avoids escaping problems.
Bu XPath expression are muc more powerful. For example fetching all description nodes (attributes and elements) from the document can be done with a single expression:
$source = new DOMDocument();
$source->loadXml($xml);
$xpath = new DOMXPath($source);
$target = new DOMDocument();
$fragment = $target->createDocumentFragment();
foreach ($xpath->evaluate('//property//description|//property//*/@description') as $node) {
$fragment
->appendChild($target->createElement('div'))
->appendChild(
$target->createTextNode(
$xpath->evaluate('string(.)', $node)
)
);
}
echo $target->saveHtml($fragment);
Output:
<div>aaa</div><div>bbb</div><div>ccc</div>
Upvotes: 0
Reputation: 8773
That's because you're doing it all wrong. Basicly you want to parse the XML instead. There are several ways to do this, but here's one of them:
if (file_exists('source.xml')){
$xml = simplexml_load_file('source.xml');
} else {
echo "Unable to load XML file!";
exit;
}
//This will output: bbb
echo $xml->example[0]['description'];
//This will output: ccc
echo $xml->example[1]['description'];
If you wish to loop through all of the example nodes / elements, you can easily do this with a foreach()
loop:
foreach($xml->example as $desc){
echo $desc['description'] ."<br />";
}
Upvotes: 1