im looking for a much easier way to iterate over a nodelist containing variable elements so i dont have to use manual assignment

e.g.

		$nodeList = $xpath->query('//product');
		$nodeLength = $nodeList->length;
		foreach ($nodeList as $nodes)
		{
			$hid    = $nodes->getElementsByTagName('id')->item(0)->nodeValue;
			$oid    = $nodes->getElementsByTagName('orderid')->item(0)->nodeValue;
			$pid    = $nodes->getElementsByTagName('pid')->item(0)->nodeValue;
			$rdate  = $nodes->getElementsByTagName('regdate')->item(0)->nodeValue;
			$status = $nodes->getElementsByTagName('status')->item(0)->nodeValue;

		$xml .= '<product>';
		$xml .= '<hid>'.$hid.'</hid>';
		$xml .= '<oid>'.$oid.'</oid>';
		$xml .= '<pid>'.$pid.'</pid>';
		$xml .= '<rdate>'.$rdate.'</rdate>';
		$xml .= '<status>'.$status.'</status>';
		$xml .= '</product>';
	}

is there a way I can use nodeName and nodeValue here to just do a one liner loop?

i've tried this...

			$xml .= '<product>';
					foreach ($nodes as $node)
					{
					$xml .= '<'.$node->nodeName.'>'.$node->nodeValue.'</'.$node->nodeName.'>';
					}
			$xml .= '</product>';

but i get a ton of empty brackets along with other issues if i dont cdata the nodeValue.. I also get stuff like #text before the nodeNames

Manual assignment, besides being tedious, is almost impossible b/c while there are a handle full of similar tags between loops, there are numerous others that each structure doesnt share that I need

any assistance would be greatly appreciated

    Untested but give it a shot:

    $nodeList = $xpath->query('//product');
    $nodeLength = $nodeList->length;
    $names = array('hid'=>'id'
    		      ,'oid'=>'orderid'
    		      ,'pid'=>'pid'
    		      ,'rdate'=>'regdate'
    		      ,'status'=>'status');
    foreach ($nodeList as $nodes)
    {
    	$xml .= '<product>';
    	foreach( $names as $k => $v ) {
    		$xml .= '<'. $k .'>'. $nodes->getElementsByTagName($v)->item(0)->nodeValue .'</'. $k .'>';
    	}
    	$xml .= '</product>';
    } 
    

      thats what i DONT want to do... the items you've placed in the array are the ONLY ones that are in every loop... i need this to be dynamic so it can pull the other 10-25 elements from each itteration

      thank you though =)

        All you need to do is check what you're dealing with.

        This xml part

        <product>
        	<hid>stuff</hid>
        </product>
        

        constains 5 nodes. The first one is product (nodename: product) which contains 3 more nodes with nodenames: #text, hid, #text, and finally the text node of hid.

        Since you don't seem to be dealing with anything nested deeper than directly as child of product, this should suffice

        $xml .= '<product>';
        	foreach ($nodes as $node)
        	{
        		if ($node->nodeName != '#text' && $node->nodeValue !== '')
        		{
        			$xml .= '<'.$node->nodeName.'>'.$node->nodeValue.'</'.$node->nodeName.'>';
        		}
        	}
        $xml .= '</product>'; 
        

        The reason is that ->nodeValue of a node contains the concatenated nodeValues of all its child nodes, and the things you are actually seening here is in the end the contents of some text node.

          odd, when I do that, i get nothing... as if it's not iterating... but if i go back to the getelementsbytagname() i get a value

          wth?

            $xml .= '<product>'; 
                foreach ($nodes as $node) 
                { 
                    if ($node->nodeName != '#text' && $node->nodeValue !== '') 
                    { 
                        $xml .= '<'.$node->nodeName.'>'.$node->nodeValue.'</'.$node->nodeName.'>'; 
                    } 
                } 
            $xml .= '</product>'; 

            The problem I see with this is that in your original post the node names do not always match the xml tag names. So even when you get this working it may still not work as desired.

              im trying to do away with my original post... i dont want to hardcode anynames... it should all be dynamic

                I get that it should be dynamic, but if you're output is being formatted by looking for hid and oid (for example) and its only seeing id and orderid it will not pick that information. Inconsistency could be causing problems when trying to update. Of course I have no idea if you are looking directly at the xml or the processed output.

                  ok i got it... my xpath query using // screwed me up in terms of inheritance...

                  how would i handle a set of nested children? i tried this without success:

                  			foreach ($nodes->childNodes as $node)
                  			{
                  				if ($node->nodeName != '#text' && $node->nodeValue != '')
                  				{
                  					if( $node->hasChildNodes == TRUE )
                  					{
                  						foreach ($node->childNodes as $childNode)
                  						{
                  							$xml .= '<'.$childNode->nodeName.'>'.$childNode->nodeValue.'</'.$childNode->nodeName.'>'."\r\n";
                  						}
                  					}
                  					else
                  					{
                  					$xml .= '<'.$node->nodeName.'>'.$node->nodeValue.'</'.$node->nodeName.'>'."\r\n";
                  					}
                  				}
                  			}
                  
                  

                  seems a bit redundant and messy as well...

                    Do you have something working without child nodes? If so, can I see that? I have an idea for a function to recursively create the xml, but I don't know what's working for you to be able to modify it to what you need.

                      scrupul0us;10987419 wrote:

                      how would i handle a set of nested children? i tried this without success:

                      You will need recursion and modify your code somewhat, since you prevoiusly wrote the element contents with $node->nodeValue, which you will rather have to handle only by taking nodevalues from text nodes while getting node names from non-text nodes.

                      The code below uses example xml data, and I start in the XML root, which means I recreate the exact same XML document, except for the actual XML opening tag.

                      <?php
                      # just some example xml data
                      $xml = <<<XML
                      <?xml version="1.0"?>
                      <products>
                      	<product>
                      		<id>1</id>
                      		<name>Alpha</name>
                      		<colors>
                      			<color>Red</color>
                      			<color>Blue</color>
                      			<color>Green</color>
                      		</colors>
                      	</product>
                      	<product>
                      		<id>2</id>
                      		<name>Bravo</name>
                      		<colors>
                      			<color>White</color>
                      			<color>Black</color>
                      		</colors>
                      	</product>
                      </products>
                      XML;
                      
                      # $root doesn't have to be the XML root, but you're looking at a (sub)tree
                      # rooted at this node anyway
                      function processTree($root)
                      {
                      	# text nodes can't have child nodes
                      	if ($root->nodeName == '#text')
                      	{
                      		# Use nodeValue from nothing but text nodes
                      		$xml = $root->nodeValue;
                      	}
                      	else
                      	{
                      		# Otherwise you have a non-text node, which means you
                      		# want its node name as start tag...
                      		$xml = sprintf('<%s>', $root->nodeName);
                      		# ... followed by its contents...
                      		foreach ($root->childNodes as $node)
                      		{
                      			$xml .= processTree($node);
                      		}
                      		# ... followed by its nodename as end tag
                      		$xml .= sprintf('</%s>', $root->nodeName);
                      	}
                      	return $xml;
                      }
                      
                      header('content-type: text/plain; charset=utf-8');
                      
                      $d = new DOMDocument();
                      $d->loadXML($xml);
                      $xp = new DOMXPath($d);
                      
                      # I selected the root node products just to check that the recursion went ok
                      $list = $xp->query('/*');
                      $xml = processTree($list->item(0));
                      
                      echo $xml;
                      ?>
                      
                        9 days later

                        Soo close! This processTree works GREAT except I need to encapsulate my data in cdata tags b/c the API im working with is pretty rough and i need to sanitize the output

                        i tried :

                            if ($root->nodeName == '#text')
                            {
                                # Use nodeValue from nothing but text nodes
                                $xml = '<![CDATA['.$root->nodeValue.']]>';
                            } 
                        

                        i end up with a bunch of empty cdata tags between each element

                          johanafm;10987399 wrote:

                          All you need to do is check what you're dealing with.

                          This xml part

                          <product>
                          	<hid>stuff</hid>
                          </product>
                          

                          constains 5 nodes. The first one is product (nodename: product) which contains 3 more nodes with nodenames: #text, hid, #text, and finally the text node of hid.

                          So if you don't want empty cdata tags where you had empty text nodes, don't include them in the first place.

                            Write a Reply...