saving parts of xml object as object - php

I created an xml file that stores some information for me. Now I want to get elements that meet some conditions.
At the moment this looks like this:
Function getElements($xmlObject, $name){
foreach($xmlObject->feature as $feature){
if(stristr($feature->path, $name))){
array_push($aSubFeatures, $feature);
}
}
return $obj;
}
But I'd prefer getting an object as a return value. I used simpleXML for getting the xml file as an object.
I also tried using DOM (creating new DOMDocument and tried to append the gotten feature element objects) but without reasonable result.
Would deleting all not matching parts of the xml a solution? Did not found a way to delete special elements...
Thanks for your help

For appending an element of a current existing DOMDocument into a new DOMDocument you have to call $newdom->importNode($nodeInOldDOM). You cannot do a regular appendChild of a node from another document.

Related

GetElementsByTagName alternative to DOMDocument

I am creating an HTML file with DOMDocument, but I have a problem at the time of the search by the getElementsByTagName method. What I found is that as I'm generating the hot, does not recognize the labels that I inserted.
I tried with DOMXPath, but to no avail :S
For now, I've got to do is go through all the children of a node and store in an array, but I need to convert that score DOMNodeList, and in doing
return (DOMNodeList) $ my_array;
generates a syntax error.
My specific question is, how I can do to make a search for tags with the getElementsByTagName method or other alternative I can offer to achieve the task?
Recalling that the DOMDocument I'm generating at the time.
If you need more information, I'll gladly place it in the question.
Sure Jonathan Sampson.
I apologize for the editing of the question the way. I did not quite understand this forum format.
For a better understanding of what I do, I put the inheritance chain.
I have this base class
abstract class ElementoBase {
...
}
And I have this class that inherits from the previous one, with an abstract function insert (insert)
abstract class Elemento extends ElementoBase {
...
public abstract function insertar ( $elemento );
}
Then I have a whole series of classes that represent the HTML tags that inherit from above, ie.
class A extends Elemento {
}
...
Now the code I use to insert the labels in the paper is as follows:
public function insertar ( $elemento ) {
$this->getElemento ()->appendChild ( $elemento->getElemento () );
}
where the function getElemento (), return a DOMElement
Moreover, before inserting the element do some validations that depend on the HTML tag that is to be inserted,
because they all have very specific specifications.
Since I'm generating HTML code at the same time, it is obvious that there is no HTML file.
To your question, the theory tells me to do this:
$myListTags = $this->getElemento ()->getElementsByTagName ( $tag );
but I always returns null, this so I researched it because I'm not loading the HTML file, because if I
$myHtmlFile = $this->getDocumento ()->loadHTMLFile ( $filename );
$myListTags = $myHtmlFile->getElementsByTagName ( $etiqueta );
I do return the list of HTML tags
If you need more information, I'll gladly place it in the question.
I am assuming you have created a valid HTML file with DOMDocument. Your basic problem is to parse or search the HTML doc for a particular tag name.
To search a HTML file the best solution available in PHP is Simple HTML DOM parser.
You can just run the following code and you are done!
$html = file_get_html('url to your html file');
foreach($html->find('tag name') as $element)
{
// perform the action you want to do here.
// example: echo $element->someproperty;
}
$doc = new DOMDocument('1.0', 'iso-8859-1');
$doc->appendChild(
$doc->createElement('Filiberto', 'It works!')
);
$nodeList = $doc->getElementsByTagName('Filiberto');
var_dump($nodeList->item(0)->nodeValue);

Get attributes from item(tag) using SimplePie

I'm trying to get attributes for "id" tag in feed with usage of simplepie.
This is the fragment of code from feed:
<updated>2012-03-12T08:26:29-07:00</updated>
<id im:id="488627" im:bundleId="dmtmobile">http://www.example.com</id>
<title>Draw Something by OMGPOP - OMGPOP</title>
I want to get number (488627) from im:id attribute contained in id tag
How can I get this ?
I tried $item->get_item_tags('','im:id') but it didn't work
If this is in an Atom 1.0 feed, you'll want to use the Atom namespace:
$data = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'id');
From there, you should then find that the attributes you want are:
$id = $data['attribs'][IM_NAMESPACE]['id']
$bundleID = $data['attribs'][IM_NAMESPACE]['bundleId']`
where IM_NAMESPACE is set to the im XML namespace (i.e. what the value of xmlns:im is).
The reason SimplePie asks for a namespace is because it internally stores the node elements under the given namespace. If you don't know what your specific namespace is, use print_r to dump it:
print_r($item->data['child']);
You can also directly access the child elements if you know the namespace, or write a simple seeker function to step through each namespace and look for a matching tag.
$data = $item->data['child']['im']['bundleId'][0]['data'];
The get_item_tags() function is stupid and doesn't usually do what you want, but it's also very simple and easy to replace with your own special purpose functions. Original source is:
public function get_item_tags($namespace, $tag)
{
if (isset($this->data['child'][$namespace][$tag]))
{
return $this->data['child'][$namespace][$tag];
}
else
{
return null;
}
}

SoapVar/Param and nested, repeated elements in SOAP

My goal is to be able to create a soap request that can contain items like so:
<flexFields>
<names>
<names>IAG Group</names>
<names>Ticket #</names>
</names>
</flexFields>
However, every combination of soapvar and soapparam I've been able to think up either makes it impossible for me to duplicate the nested 'names' tag. I can get 1 sub tag like so:
$flexFields = array(
'names'=> new SoapVar(
new SoapVar(array('names'=>'IAG Group'),SOAP_ENC_OBJECT),
SOAP_ENC_OBJECT)
);
This generates:
<flexFields xsi:type="ns2:SoapNamedValues">
<names xsi:type="names">
<names xsi:type="xsd:string">IAG Group</names>
</names>
</flexFields>
But any attempt I make to get the names tag to repeat either generates a dreaded BOGUS element if I use SOAP_ENC_OBJECT, or wraps every item in another 'item' element if I use SOAP_ENC_ARRAY, which is also not desirable.
I know I could just manually create what I want and load it with XSD_ANYXML, but that is getting close to the line of defeating the purpose of using the SOAP library.
Can anyone provide an example of just how to perfectly balance the soapvar/soapparam + array nesting to get this to actually work? Or am I attempting the impossible with PHP's SOAP library?
I have a similar problem, try this:
$Names=array();
$Names[]=new SoapVar("IAG Group",XSD_STRING,null,null,'names');
$Names[]=new SoapVar("Ticket #",XSD_STRING,null,null,'names');
$BigNames=new SoapVar($Names,SOAP_ENC_OBJECT,null,null,'Names');
This creates and array of of SoapVar objects ($Names) and places them in the BigNames object, creating an output like this:
<Names>
<names>IAG Group</names>
<names>Ticket #</names>
</Names>
You can then create another SoapVar object for FlexFields, but for some reason you can't place a SoapVar object directly into another, it has to be stored in an array...
I want to do this:
$FlexFields=new SoapVar($BigNames,SOAP_ENC_OBJECT,null,null,'FlexFields');
This works:
$FF=array($BigNames);
$FlexFields=new SoapVar($FF,SOAP_ENC_OBJECT,null,null,'FlexFields');
I ran into the BOGUS tag problem also. My solution involved using an ArrayObject in place of array primitives. The objects are all then converted to SoapVar objects. It seems the soap library really wants to deal with objects everywhere. I have a more complete writeup here:
http://www.fischco.org/blog/2011/3/26/php-soapserver-objects-arrays-and-encoding.html

Error when merging two XML documents using XPath & DOMDocument

About a year ago I wrote a jQuery-inspired library which allowed you to manipulate the DOM using PHP's XPath and DOMDocument. I recently wanted to clean it up and post it as an open source project. I've been spending the past few days making improvements and implementing some more of PHP's native OO features.
Anyhow, I thought I'd add a new method which allows you to merge a separate XML document with the current one. The catch here is that this method asks for 2 XPath expressions. The first one fetches the elements you want to merge into the existing document. The second specifies the destination path of these merged elements.
The method works well in fetching matching elements from both paths, but I'm having issues with importing the foreign elements into the current DOM. I keep getting the dreaded 'Wrong Document Error' message.
I thought I knew what I was doing, but I suppose I was wrong. If you look at the following code, you can see that I'm first iteration through the current documents matching elements, then through the foreign document's matching elements.
Within the second nested loop is where I am attempting to merge each foreign element into the destination path in the current document.
Not sure what I'm doing wrong here as I'm clearly importing the foreign node into the current document before appending it.
public function merge($source, $path_origin, $path_destination)
{
$Dom = new self;
if(false == $Dom->loadXml($source))
{
throw new DOMException('XML source could not be loaded into the DOM.');
}
$XPath = new DOMXPath($Dom);
foreach($this->path($path_destination, true) as $Destination)
{
if(false == in_array($Destination->nodeName, array('#text', '#document')))
{
foreach($XPath->query($path_origin) as $Origin)
{
if(false == in_array($Destination->nodeName, array('#text', '#document')))
{
$this->importNode($Origin, true);
$Destination->appendChild($Origin->cloneNode(true));
}
}
}
}
return $this;
}
You can find the library in its entirety in the following Github repo:
http://github.com/wilhelm-murdoch/DomQuery
Halps!!!
importNode doesn't "change" the node so it belongs to another document. It creates a new node belonging to the new document and returns it. So you should be getting its return value and using that in appendChild.
$Destination->appendChild($this->importNode($Origin, true));

Using DOMXPath to replace a node while maintaining its position

Ok, so I had this neat little idea the other night to create a helper class for DOMDOCUMENT that mimics, to some extent, jQuery's ability to manipulate the DOM of an HTML or XML-based string. Instead of css selectors, XPath is used. For example:
$Xml->load($source)
->path('//root/items')
->each(function($Context)
{
echo $Context->nodeValue;
});
This would invoke a callback function on every resulting node. Unfortunately, PHP version < 5.3.x doesn't support lambda functions or closures, so I'm forced to do something a bit more like this for the time being:
$Xml->load($source)
->path('//root/items')
->walk('printValue', 'param1', 'param2');
Everything is working great at the moment and I think this project would be useful to a lot of people, but I'm stuck with one of the functions. I am attempting to mimic jQuery's 'replace' method. Using the following code, I can accomplish this quite easily by applying the following method:
$Xml->load($source)
->path('//root/items')
->replace($Xml->createElement('foo', 'bar')); // can be an object, string or XPath pattern
The code behind this method is:
public function replace($Content)
{
foreach($this->results as $Element)
{
$Element->parentNode->appendChild($Content->cloneNode(true));
$Element->parentNode->removeChild($Element);
}
return $this;
}
Now, this works. It replaces every resulting element with a cloned version of $Content. The problem is that it adds them to the bottom of the parent node's list of children. The question is, how do I clone this element to replace other elements, while still retaining the original position in the DOM?
I was thinking about reverse-engineering the node I was to replace. Basically, copying over values, attributes and element name from $Content, but I am unable to change the actual element name of the target element.
Reflection could be a possibility, but there's gotta be an easier way to do this.
Anybody?
Use replaceChild instead of appendChild/removeChild.
Lookup if $element has a nextsibbling prior to removing if so do an insertBefore that next sibling otherwise simply append.
public function replace($Content)
{
foreach($this->results as $Element)
{
if ($Element->nextSibling) {
$NextSiblingReference = $Element->nextSibling;
$Element->parentNode->insertBefore($Content->cloneNode(true),$NextSiblingReference);
}
else {
$Element->parentNode->appendChild($Content->cloneNode(true));
}
$Element->parentNode->removeChild($Element);
}
return $this;
}
Totally untested though.
Or as AnthonyWJones suggested replaceChild , big oomph how did i miss that moment :)

Categories