This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to insert HTML to PHP DOMNode?
PHP and DOMDocument
I am trying to write a Class that converts an array to XML using DOMDocument - and on top of that imports HTML into the DOM document. Problem is that the HTML is not imported into the DOM document - it gets imported as a text string (for instance, HTML tags are shown as <p> instead of <p> in the source for the resulting XML document).
Update:
Code added directly to this Question as requested by Hakre. The code is a bit hacked but works - it would be interesting though to get rid of the extend from DomDocument as suggested by Hakre.
class xmlizer extends DomDocument {
function __construct() {
parent::__construct();
}
function node_create($arr, $items = null) {
if (is_null($items))
$items = $this->appendChild($this->createElement("items"));
// Loop the array values.
foreach($arr as $element => $value) {
// If Array has numeric keys, use "node - else use $element.
$element = is_numeric( $element ) ? "node" : $element;
// Create element, add $value unless $value is an array - and append to main object ($items).
$fragment = $this->createElement($element, (is_array($value) ? null : $value));
$items->appendChild($fragment);
// Iterate if $value is an array, .
if (is_array($value)) {
self::node_create($value, $fragment);
}
}
}
public function __toString() {
// html_entity_decode() added by Micha. Thanks.
return html_entity_decode($this->saveXML());
}
}
// Build test Array with HTML string (for testing purposes only).
for($i=0;$i<3;$i++) {
$j = $i+1;
$array['example'][] = array(
"id" => $j,
"title" => "Title $j",
"description" => "<p>Text <strong>string</strong> nr. $j with <em>some</em> <code>HTML code</code>.</p>",
);
}
// Test: Run the code.
header("Content-Type:text/xml");
$xml = new xmlizer();
$xml->node_create($array);
echo $xml;
PS: Please don't close the Question as I don't think this is a duplicate. Thanks.
Try html_entity_decode($value) on line 15 in the second code but why you want the HTML as HTML because then it would be interpreted as XML.
Update
Sorry the one above doesn't work and this doesn't work too:
$this
->createElement($element)
->createTextNode(is_array($value) ? null : $value ));
Finaly I tryed it my self:
I think this is the best solution: http://codepad.org/PpyewkVd
Related
I try to foreach loop XML file. First it is turned to JSON from XML file and then to PHP array that I am trying to loop with foreach function (With Zend Framework).
Problem is when XML file haves only one activity then whole code not work and when XML file haves a more than one activity then code works:
<?php
header('Content-type: application/javascript; charset=utf-8');
set_include_path(implode(PATH_SEPARATOR, array(
'library',
get_include_path()
)));
require_once('Zend/Loader.php');
Zend_Loader::loadClass('Zend_Loader_Autoloader');
$autoloader = Zend_Loader_Autoloader::getInstance();
$xmlStringContents = file_get_contents("activities.xml");
$jsonContents = Zend_Json::fromXml($xmlStringContents, true);
$decodedValues = Zend_Json::decode($jsonContents);
$project[0] = array("key" => "", "value" => "Please select...");
$i = 1;
foreach ($decodedValues['Envelope'] as $key => $value) {
foreach ($value as $keyEnvelope => $valueEnvelope) {
if (is_array($valueEnvelope)) {
foreach ($valueEnvelope['EFIAIFProjActivity'] as $keyEFIAIFProjTable => $valueEFIAIFProjTable) {
if (is_array($valueEFIAIFProjTable)) {
foreach ($valueEFIAIFProjTable as $projectValue) {
if (isset($_GET['project'])) {
if ($_GET['project']==$projectValue['Project']['ProjId'] && $_GET['project'] != "") {
$project[$i] = array("key" => $projectValue['smmActivities']['ActivityNumber'], "value" => $projectValue['smmActivities']['PSADescription']);
$i++;
}
}
}
}
}
}
}
}
if ($i==1) {
$project[$i] = array("key" => "NO_ACTIVITY_000001", "value" => "No Activity");
}
$projects = array("values" => $project);
if (isset($_GET['callback'])) {
echo $_GET['callback']."(";
echo Zend_Json::encode($projects);
echo ")";
}
I need to get this code work like that way it makes a JSON string with "Please select..." and "No Activity" to Activity dynamic drop down menu if dynamic drop down menu Project selected project haves no activities in Atlassian JIRA and Tempo Add-on. And if selected project haves one or more activities in XML file then Activity drop down menu haves these activities.
The easy solution is to not convert the XML to JSON. SimpleXML allows you to use object/traversable syntax on the XML DOM. So if you access a child element as object property is is treated as a single value, if you access as as a list (with foreach) it is treated as a list.
The main difference to you current solution is that you have to use object property syntax, not array syntax.
Another thing is that you code looks like it reads SOAP. While SOAP uses a XML syntax for serialization, it is NOT "just XML". You're better of using a specific SOAP library.
If XML has single node then Zend Framework converts it to JSON as key value pair.
If XML has more than one node then Zend Framework converts it to a JSON array.
You can handle this in 2 ways.
Check if its an array then loop it else access the values directly by its key
If the node is not an array, convert it to an array after its decoded.
I'm parsing some XML with PHP DOM extension in order to store the data in some other form. Quite unsurprisingly, when I parse an element I pretty often need to obtain all children elements of some name. There is the method DOMElement::getElementsByTagName($name), but it returns all descendants with that name, not just immediate children. There is also the property DOMNode::$childNodes but (1) it contains node list, not element list, and even if I managed to turn the list items into elements (2) I'd still need to check all of them for the name. Is there really no elegant solution to get only the children of some specific name or am I missing something in the documentation?
Some illustration:
<?php
DOMDocument();
$document->loadXML(<<<EndOfXML
<a>
<b>1</b>
<b>2</b>
<c>
<b>3</b>
<b>4</b>
</c>
</a>
EndOfXML
);
$bs = $document
->getElementsByTagName('a')
->item(0)
->getElementsByTagName('b');
foreach($bs as $b){
echo $b->nodeValue . "\n";
}
// Returns:
// 1
// 2
// 3
// 4
// I'd like to obtain only:
// 1
// 2
?>
simple iteration process
$parent = $p->parentNode;
foreach ( $parent->childNodes as $pp ) {
if ( $pp->nodeName == 'p' ) {
if ( strlen( $pp->nodeValue ) ) {
echo "{$pp->nodeValue}\n";
}
}
}
An elegant manner I can imagine would be using a FilterIterator that is suitable for the job. Exemplary one that is able to work on such a said DOMNodeList and (optionally) accepting a tagname to filter for as an exemplary DOMElementFilter from the Iterator Garden does:
$a = $doc->getElementsByTagName('a')->item(0);
$bs = new DOMElementFilter($a->childNodes, 'b');
foreach($bs as $b){
echo $b->nodeValue . "\n";
}
This will give the results you're looking for:
1
2
You can find DOMElementFilter in the Development branch now. It's perhaps worth to allow * for any tagname as it's possible with getElementsByTagName("*") as well. But that's just some commentary.
Hier is a working usage example online: https://eval.in/57170
My solution used in a production:
Finds a needle (node) in a haystack (DOM)
function getAttachableNodeByAttributeName(\DOMElement $parent = null, string $elementTagName = null, string $attributeName = null, string $attributeValue = null)
{
$returnNode = null;
$needleDOMNode = $parent->getElementsByTagName($elementTagName);
$length = $needleDOMNode->length;
//traverse through each existing given node object
for ($i = $length; --$i >= 0;) {
$needle = $needleDOMNode->item($i);
//only one DOM node and no attributes specified?
if (!$attributeName && !$attributeValue && 1 === $length) return $needle;
//multiple nodes and attributes are specified
elseif ($attributeName && $attributeValue && $needle->getAttribute($attributeName) === $attributeValue) return $needle;
}
return $returnNode;
}
Usage:
$countryNode = getAttachableNodeByAttributeName($countriesNode, 'country', 'iso', 'NL');
Returns DOM element from parent countries node by specified attribute iso using country ISO code 'NL', basically like a real search would do. Find a certain country by it's name in an array / object.
Another usage example:
$productNode = getAttachableNodeByAttributeName($products, 'partner-products');
Returns DOM node element containing only single (root) node, without searching by any attribute.
Note: for this you must make sure that root nodes are unique by elements' tag name, e.g. countries->country[ISO] - countries node here is unique and parent to all child nodes.
This question already has an answer here:
Getting the value of brother/sister node
(1 answer)
Closed 9 years ago.
So I was playing with domDocs in php and I was going through structure of many nodes. When the script finds APP_ID it was looking for, he then need to return his brother value, APP_USER.
I found no solution on here, only XPath and jQuery that I find 'avoiding' of how it meant to be used.
It's very simple
Before you call foreach, put one iterating variable e.g. $i which will then 'call' the brother's value.
$apps = $root->getElementByTagName( 'APP_ID' );
$i=0
foreach( $apps as $app ) {
if( $app->item(0)->nodeValue == CONSTANT-ID ) { // just condition
$user = $root->getElementsByTagName( "APP_USER" );
echo $user->item($i)->nodeValue;
// this $i means it returns brother's value
}
$i++;
}
what do you think?
Starting from $app, you can use parentNode to go up one level and then iterate over childNodes to find the sibling APP_USER:
if( $app->nodeValue == CONSTANT-ID ) { // just condition
foreach ($app->parentNode->childNodes as $child) {
if ($child->localName == 'APP_USER') {
echo $child->nodeValue;
}
}
}
I'm using DOMi ( http://domi.sourceforge.net ) to create XML from arrays.
But I don't know how to create attributes in these XML (in arrays, so these attributes appear in the XML). How can I construct these arrays so I can get some tags with attributes after the convertion?
Thank you!
Looking at the source code, apparently you pass the second argument "attributes" to attachToXml:
public function attachToXml($data, $prefix, &$parentNode = false) {
if(!$parentNode) {
$parentNode = &$this->mainNode;
}
// i don't like how this is done, but i can't see an easy alternative
// that is clean. if the prefix is attributes, instead of creating
// a node, just put all of the data onto the parent node as attributes
if(strtolower($prefix) == 'attributes') {
// set all of the attributes onto the node
foreach($data as $key=>$val)
$parentNode->setAttribute($key, $val);
$node = &$parentNode;
}
//...
}
I want to write a function that parses a (theoretically) unknown XML data structure into an equivalent PHP array.
Here is my sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<content>
<title>Sample Text</title>
<introduction>
<paragraph>This is some rudimentary text</paragraph>
</introduction>
<description>
<paragraph>Here is some more text</paragraph>
<paragraph>Even MORE text</paragraph>
<sub_section>
<sub_para>This is a smaller, sub paragraph</sub_para>
<sub_para>This is another smaller, sub paragraph</sub_para>
</sub_section>
</description>
</content>
I modified this DOM iterating function from devarticles:
$data = 'path/to/xmldoc.xml';
$xmlDoc = new DOMDocument(); #create a DOM element
$xmlDoc->load( $data ); #load data into the element
$xmlRoot = $xmlDoc->firstChild; #establish root
function xml2array($node)
{
if ($node->hasChildNodes())
{
$subNodes = $node->childNodes;
foreach ($subNodes as $subNode)
{
#filter node types
if (($subNode->nodeType != 3) || (($subNode->nodeType == 3)))
{
$arraydata[$subNode->nodeName]=$subNode->nodeValue;
}
xml2array($subNode);
}
}
return $arraydata;
}
//The getNodesInfo function call
$xmlarray = xml2array($xmlRoot);
// print the output - with a little bit of formatting for ease of use...
foreach($xmlarray as $xkey)
{
echo"$xkey<br/><br/>";
}
Now, because of the way I'm passing the elements to the array I'm overwriting any elements that share a node name (since I ideally want to give the keys the same names as their originating nodes). My recursion isn't great... However, even if I empty the brackets - the second tier of nodes are still coming in as values on the first tier (see the text of the description node).
Anyone got any ideas how I can better construct this?
You might be better off just snagging some code off the net
http://www.bin-co.com/php/scripts/xml2array/
/**
* xml2array() will convert the given XML text to an array in the XML structure.
* Link: http://www.bin-co.com/php/scripts/xml2array/
* Arguments : $contents - The XML text
* $get_attributes - 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value.
* $priority - Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance.
* Return: The parsed XML in an array form. Use print_r() to see the resulting array structure.
* Examples: $array = xml2array(file_get_contents('feed.xml'));
* $array = xml2array(file_get_contents('feed.xml', 1, 'attribute'));
*/
function xml2array($contents, $get_attributes=1, $priority = 'tag') {
You might be interested in SimpleXML or xml_parse_into_struct.
$arraydata is neither passed to subsequent calls to xml2array() nor is the return value used, so yes "My recursion isn't great..." is true ;-)
To append a new element to an existing array you can use empty square brackets, $arr[] = 123; $arr[$x][] = 123;
You might also want to check out XML Unserializer
http://pear.php.net/package/XML_Serializer/redirected