I'm using SimpleXML to pull in a XML feed. I need to grab the second node with the same name.
Feed example is:
<parent-node>
<child-node>
<the-node>
<the-target>Text</target>
</the-node>
<the-node>
<the-target>Text</target>
</the-node>
</child-node>
</parent-node>
Since the nodes I am targeting use hyphens, I need to use bracket syntax
$item->{'parent-node'}->{'child-node'}->{'the-node'}
This will grab the first <the-node>
When using the bracket syntax, I cannot select the second <the-node>'s <the-target> using any of the following...
$item->{'parent-node'}->{'child-node'}->{'the-node[2]'}->{'the-target'}
$item->{'parent-node'}->{'child-node'}->{'the-node'[2]}->{'the-target'}
$item->{'parent-node'}->{'child-node'}->{'the-node'}[2]->{'the-target'}
My question is, how do I target childIndex while using the bracket syntax to grab the second <the-node>'s <target>?
--- UPDATES ---
After some of the answers, I have tried the following with no luck
$item->{'parent-node'}->{'child-node'}->{'the-node'}[1]->{'the-target'}
$item->{'parent-node'}->{'child-node'}->{'the-node'}->{'the-target'}[1]
$item->{'child-node'}->{'the-node'}->{'the-target'}[1]
SimpleXMLElement stores sibling nodes as if they were an array. This usually indicates that the values are stored with a 0-based index, ie. the first value in the array starts at index 0.
So in this case, the 2nd sibling node is only accessible using the index 1 instead of 2.
Also the root-level node does not have to be traversed into by default (unless you omitted some other XML or are using some non-default settings).
Try this:
// Will grab the 2nd <the-node/>
$node = $item->{'child-node'}->{'the-node'}[1];
Depending on if your initial code did work without the array access, you can try this too:
// Testing locally I was not able to use this and got an error
// But maybe things are omitted in your question.
$node = $item->{'parent-node'}->{'child-node'}->{'the-node'}[1];
The correct syntax looks like the following:
$item->{'child-node'}->{'the-node'}[0]; // First the-node
$item->{'child-node'}->{'the-node'}[1]; // Second the-node
If parent-node is the root element of all others, you cannot access it explicitly.
$item->{'parent-node'}->{'child-node'}->{'the-node'}[0];
The above code will result in error saying "Trying to get property of non-object".
Since parent-node is the top root element, you cannot access it explicitly.
Only the immediate child element of the top root element will be available to access in the SimpleXMLElement object.
Related
so I have this xml
<xmlTag>
<item>etc etc</item>
<section>etc etc</section>
<item>etc etc</item>
<item>etc etc</item>
<section>etc etc</section>
<xmlTag>
and I want to process this in order, ie process the first item tag then the second section tag then the third item tag...etc
however when I use simplexml_load_string() the resultant object becomes
$xmlTag = {SimpleXMLElement}[2]
item = {array} [3]
section = {array} [2]
Hence it separates out the item tags and the section tags and now I have no way to determine the orderings between the item tags and the section tags....
Anyone know of an alternative way to figure out the order of the xml elements in this scenario?
Whatever dump function you're using there is misleading you (anything not dedicated to the purpose will, due to the large amount of "magic" supported by SimpleXML). The nodes have not been permanently separated out, it's just showing you that they can be accessed that way.
If you use the children() method, you will get them in the order they are defined in the document, regardless of tag name:
foreach ( $xmlTag->children() as $child_name => $child ) {
echo $child_name, "\n";
}
Note that children() doesn't actually return an array, just an "iterable" object. So unlike a real array, the same "key" can occur multiple times when you loop over it.
I've seen this problem a few times, and would like a definitive answer.
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'. var_dump (unsurprisingly) does not show any attributes for ByteListSet. Indeed, foreach iteration does not produce a #attributes item.
However, the following structure:
<ByteListSet>
<Byte Order="0"><Value>3729</Value></Byte>
</ByteListSet>
Results properly in ByteListSet having a child Byte that is a SimpleXmlObject which has #attributes.
I would assume that SimpleXML is indeed picking up the #attributes from the first case, but where is it keeping them? The trouble is that in the former structure, ByteListSet produces this on var_dump of ->children():
object(SimpleXMLElement)[25]
public 'Byte' => string '14' (length=2)
if I get_object_vars() on it and var_dump each, I simply get:
string '14' (length=2)
Indicating that Byte is not being returned to me as an xml object, but just as a string; as a property of the ByteList object above it.
Order="0" is there somewhere, but I don't have access to it. How do I get to it? NOTE: ->attributes() returns, as you would expect, a blank array.
(We do not control this schema, so it can't be restructured.)
You wrote:
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'.
Sure, because the attribute order does not exist. XML is case-sensitive, the attribute is named Order with uppercase O at the beginning.
Using the right attribute name allows you to access the value as outline in Example #5 in the basic examples of the SimpleXML extension you can find in the manual:
$ByteListSet = simplexml_load_string(<<<XML
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
XML
);
echo $ByteListSet->Byte['Order']; # 0
Please keep in mind that what you see in var_dump or print_r is not always what you get. This is espeically true for Simplexml (compare: How to get values of xml elements?; SimpleXML and print_r() - why is this empty?), however, also for other objects in PHP that make use of ArrayAccess, __toString() or IteratorIterator / IteratorAggregate.
Please let me know if that answers your question. You were not very specific which existing solutions you have not understood so far, so I answered your question, but I'm not 100% if I hit the nail.
I have a bunch of dom manipulation functions within a class.
One of those functions assigns unique ids to specific nodes.
$resource_info_node->setAttribute('id', 'resource_'.$this->ids);
$details['id'] = 'resource_'.$this->ids;
$details['node'] = $resource_info_node;
$this->resource_nodes['resource_'.$this->ids] = $details;
$this->ids += 1;
later I want to look up and modify those nodes.
I have tried :
$current_node = $this->resource_nodes[$id]['node'];
When I print_r() I find that this node is a duplicate of the original node.
It has the original node's attributes but is not a part of the DOM tree.
I get the same results with :
$this->content->getElementById($id);
I suppose I based this whole thing on storing node references in an array. I thought that was a fine thing to do. Even if not, after that using getElementByID() should have returned the node within the dom.
I thought that, in PHP all objects were passed by reference. Including DOM nodes.
Any ideas on how I can test what is actually going on.
EDIT :
Well I used :
$this->xpath->query('//*[#id]');
That returned the right number of items with ids. The node is just not in the DOM tree when I edit it.
and
$current_node = &$this->resource_nodes[$id]['node'];
Using the reference syntax had no affect.
The strangest part is that get elementById() is not returning a node in the dom. It has all the right attributes except no parentNode.
FIX - not answer :
I just used xpath instead of my reference or getElementById().
Use reference explicity:
$current_node = &$this->resource_nodes[$id]['node'];
And modify $current_node
I have a node reference field which I can output using echo render( $content['field_link'] );
This is fine for one situation in the node but I also need to output just the path of that node. I can output it using echo $node->field_link['und'][0]['node']->uri['path'] but I don't want to hard code the 'und' and '0' array keys in. There is presumably a way to do it with render().
If someone could point me in the right direction that'd be great, thanks.
Ben
You can't do it with render but you don't need to hard code the language code, you can get it from the global variables:
global $language;
echo $node->field_link[$language->language][0]['node']->uri['path']
You won't be able to get around using the 0 key though, all fields are stored with the potential to be multiple so you will always need to 'pick' which element you want to get.
If the cardinality of your field is 1, you can always assume that the element you're looking is for is at field_link[$language->language][0]. If not you'd need to run through each element in the und array and decide which one to display.
EDIT
You can also use the LANGUAGE_NONE constant (which will generally return 'und', but either way wil be the correct language code for the default content).
<contact:addr>
<contact:street></contact:street>
<contact:street></contact:street>
<contact:street></contact:street>
<contact:city></contact:city>
<contact:pc></contact:pc>
<contact:cc></contact:cc>
</contact:addr>
On the example above we can see that we do have three times the element street;
Is there a way, by using simpleXML, to properly access, for example, the second street element?
Thanks in advance,
MEM
The element reference in SimpleXML can be accessed as an array (since it is an iterator), meaning that $root->element[1] will return the second element with name "element" under the root. (and [0] will return the first, as shown in the SimpleXML examples in the PHP manual.)
You can iterate over all the elements using foreach($root->element as ..)