Does anyone know of a tool, script some of that nature that will map a multi-level XML file to a custom object? Using SimpleXML I can get a StdObject, but need to map it to my object.

Visual Studio has a great tool for this called XSD which makes it all seemless. Need to do this with PHP. Anythoughts?

Thanks in advance

    It can be handled so easily that you need nothing but a few lines of php to do so yourself.

    class Person
    {
    	private $name;
    	private $age;
    
    public function __construct() { }
    
    public function __set($k, $v)
    {
    	$this->$k = $v;
    }
    
    
    public function setName($v)
    {
    	$this->name = $v;
    }
    public function setAge($v)
    {
    	$this->age = $v;
    }
    
    public function __get($k)
    {
    	return $this->$k;
    }
    }
    
    # Example. Uses magic setter function __set
    $arr = array
    (
    	'name'	=> 'joe',
    	'age'	=> '30'
    );
    $stdo = (object) $arr;
    
    $o = new Person();
    foreach ($stdo as $k => $v)
    {
    
    $o->$k = $v;
    }
    echo 'name: ' . $o->name . '<br/>age: ' . $o->age . '<br/>';
    
    # New example. Uses specific setters setName() and setAge()
    $arr = array
    (
    	'name'	=> 'jane',
    	'age'	=> '25'
    );
    $stdo = (object) $arr;
    
    foreach ($stdo as $k => $v)
    {
    	$func = 'set'.$k;
    	$o->$func($v);
    }
    echo 'name: ' . $o->name . '<br/>age: ' . $o->age . '<br/>';
    
    # Example for when class properties names doesn't match those in your std Object
    $arr = array
    (
    	'firstName'	=> 'joe',
    	'age'	=> '30'
    );
    $stdo = (object) $arr;
    
    $o = new Person();
    $properties = array
    (
    	'firstName'	=> 'name',
    	'age'		=> 'age'
    );
    foreach ($stdo as $k => $v)
    {
    
    $o->$properties[$k]= $v;
    }
    echo 'name: ' . $o->name . '<br/>age: ' . $o->age . '<br/>';
    

      Think I was looking for something more generic which handled multi-level XML. I am writing my own parser which reliies on 3 things, the original XML file, and XML Schema and a php object.

      I define all the object namespaces in the Schema (with attributes), iterate through the xml file and build up my object. Getting close to finishing it now. Would still like to see if anything else exists.

      Now I am using my own custom attribute settings, but will see if I can use XML Schema Standards to mesh with my script instead of creating something new.

        linchatter;10987288 wrote:

        Think I was looking for something more generic

        I don't see how it would get more generic than this. Actually, the inverse is more likely to be needed, where some special care might be needed for some property. That special care would go either into a setSomeAttribute() function, or into a special case in the __set() function.

        linchatter;10987288 wrote:

        which handled multi-level XML.

        Do the job recursively and it doesn't matter how many nested objects you have, or put this conversion in the __set() or setSomePropertyWhichIsClassInstance().

        The "only" thing you lack in an stdClass instance that you get in your particular class whatever it is, is the stuff which is generic to your class: statics properties, functions, constants and visibility modifiers (private, protected, public). What you have in the stdClass instance, without visibility modifiers, is whatever is particular to this class instance: the data. And that's why all you need to do is map this data into the properties of your class.
        Assuming the property identifiers are identical, the process can be completely automated without handling of any special cases. For nested object, and assuming the property identifiers also match a classname, the entire process can still be completely automated without any special case handling.

        It's only if some property identifier in stdClass doesn't match the same property identifier in yourClass, or if some property is an object while its identifier doesn't match the classname that this object should be transformed into that you need to handle special stuff. And that's easily handled by using an array or other similar structure that maps stdClass property identifiers into yourClass property identifiers and/or classnames.

        So I still don't get what the point would be in spending time trying to find what you're looking for. As far as I understand your needs, I gave it to you, and it took 30 seconds to write the code for it, with another few minutes to write code for example data and the Person class, which is also just created as an example. You have your data (not coming from an array initially, but either way ending up as a standard object before the transform begins), and you have your end class(es).

        Sure, dealing with the nested objects might take another minute to code, but it's along the lines of adding code for it in its specific setSomeObjectProperty() function, or in the __set() function: if ($propIdentifier == 'foo') { transform($propIdentifier, getClassNameForProp($propIdentifier) }, where transform() would be the function handling the transformation, whose code was not wrapped in a function in my little example.
        Add in a third parameter, array $identifierMappings, and you're all set.

          I should have explained a little better. Say my properties do no match that of the incoming XML file, so I need to map the XML Node Names to my Object (the current code below does not reflect that ability). The code NOW maps an XML file with same nodes to object with identical property names.

          End result, iterate the XML file, check the DTD (or DTD) and produce and object. I see what your saying and appreciate the time you spent coding and explaing it.

          I need something a little more complex. Also I want to define integers, stings and arrays.

          The code you supplied is good, but will not handle mapping inconsistent field names. This is where I am hitting a snag.

          $def = '<content><OptionsTable pType="object" class="OptionsTable" xmlns="">
              <OptionRecord pType="array" class="OptionRecord" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
                <CompanyNumber pType="string">ZE7</CompanyNumber>
                <OptionCode pType="string">Nail</OptionCode>
                <OptionDesc pType="object" type="tools">Stainless</OptionDesc>
                	<tools pType="array" type="tool">
                		<tool ptype="string">hammer</tool>
                	</tools>     	      			      		
              </VehicleOptionsTable>
          </content>
          ';
          
          $xml = '<content id="Content0">
                    <OptionsTable xmlns="">
          		    <OptionRecord xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          		      <CompanyNumber >ZE7</CompanyNumber>
          		      <OptionCode >Nail</OptionCode>
          		      <OptionDesc >Stainless</OptionDesc>
                			<tools>
                				<tool>hammer</tool>
                			</tools>     	      			      		
          		    </OptionRecord>
          		    <OptionRecord xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          		      <CompanyNumber >ZE7</CompanyNumber>
          		      <OptionCode >Nail</OptionCode>
          		      <OptionDesc >Stainless</OptionDesc>
                			<tools>
                				<tool>hammer</tool>
                			</tools>     	      			      		
          		    </OptionRecord>                 
          </OptionsTable> </content> '; function iterate($SimpXmlObject, $SimpXmlObjDef, $nodePath, $objRef) { if (count($nodePath) == 0) { $SimpXmlObject = $SimpXmlObject->children(); array_push($nodePath, $SimpXmlObject->getName()); $SimpXmlObject = $SimpXmlObject->children(); } foreach($SimpXmlObject as $key=>$val) { //Push the current node onto the end of the nodePath array. This is what generates the current node path. array_push($nodePath, $key); $xpath = implode("/", $nodePath); $obj = $SimpXmlObjDef->xpath($xpath); $attrPType = (string)$obj[0]->attributes()->pType; $attrclass = (string)$obj[0]->attributes()->class; $nodeName = $val->getName(); print "$xpath = $attrPType = $attrclass = $nodeName"; print "<br>"; if ($attrPType == "object") { $a = $objRef->$nodeName = new $attrclass(); iterate($val, $SimpXmlObjDef, $nodePath, $a); } if ($attrPType == "array") { $objRef->$nodeName = array(); if (! isset($i)) $i=0; foreach($val as $key1=>$val1) { $i++; print "<br>{$i}<br>"; $bob = new $attrclass(); iterate($val, $SimpXmlObjDef, $nodePath, $bob); $arr[$i] = $bob; } $objRef->$nodeName = $arr; } if ($attrPType != "object" and $attrPType != "array") { $objRef->$nodeName = (string)$val[0]; } array_pop($nodePath); } } $simpXmlObject = simplexml_load_string($xml); $simpXmlObjectDef = simplexml_load_string($def); $objOptionsTable = new OptionsTable(); iterate($simpXmlObject, $simpXmlObjectDef, $path, $objOptionsTable); print_r($objVehicleOptionsTable);
            linchatter;10987303 wrote:

            Say my properties do no match that of the incoming XML file

            johanafm;10987247 wrote:
            class Person
            {
            	private $name;
            	private $age;
            }
            $arr = array
            (
            	'firstName'	=> 'joe',
            	'age'	=> '30'
            );
            $stdo = (object) $arr;
            $o = new Person();
            
            $properties = array
            (
            	'firstName'	=> 'name',
            	'age'		=> 'age'
            );
            foreach ($stdo as $k => $v)
            {	
            	$o->$properties[$k]= $v;
            }
            

            Like I said, it's easy to map from one node name to some other class property identifier.

            linchatter;10987303 wrote:

            I need something a little more complex. Also I want to define integers, stings and arrays.

            Well, I'm not talking about parsing an XML file. I'm talking about turning the stdClass instance(s) which you've allready created while parsing the XML into instances of whatever class you see fit.

            linchatter;10987303 wrote:

            The code you supplied is good, but will not handle mapping inconsistent field names.

            yes it does.

              Below is an example. Have an idea of what you are talking about, but not following the hierarchical issues in the object and the XML.

              class a {
                 $dog;
                 $cat;    
              } class b { /** @var a */ $animals; /** @var string */ $location; } <content> <stores> <store> <location>usa</location> <animals> <dog>Y</dog> <cat>Y</cat> </amimals> <store> </stores> </content>
                linchatter;10987314 wrote:

                Below is an example. Have an idea of what you are talking about, but not following the hierarchical issues in the object and the XML.

                Well, earlier you claimed to have StdClass objects, which naturally lead me to assume you were able to process the XML. I simply showed how to turn StdClass objects into other objects. And once you understand how to turn stuff into your particular objects, coding this should be trivial.

                But I assure you, my approach really really is able to handle hierarchies of objects, arrays and variables. Below I also give you the approach to handling the XML tree to build up hierarchies of obects, arrays and variables. All you need to do now is combine this information with what I've previously posted.

                Apart from that, what is the meaning of class a and why is it called a?

                class a {
                   $dog;
                   $cat;    
                }

                Also, looking at the XML

                          <animals>
                              <dog>Y</dog>
                              <cat>Y</cat>
                           </amimals>
                

                Tells me that animals should be represented as an array, and that it should contain the 2 elements named <animal> with node values of "dog" and "cat" respectively, i.e.

                          <animals>
                              <animal>dog</animal>
                              <animal>Y</animal>
                           </amimals>
                

                But if you havn't dealt with processing an XML tree, I recently posted some simple code here that does just that. It's just that it does nothing other than go over a DOM tree and recreate the XML it was built from (apart from not dealing with attributes).

                And some comments on that code for your particular benefit

                # class definitions here
                
                # here comes the same function as posted in the other thread, but with some
                # comments on how to modify it
                function processTree($root)
                {
                    # this part can be removed
                    if ($root->nodeName == '#text')
                    {
                        return $root->nodeValue;
                    }
                	# Obviously you don't want an XML opening tag. You want... an object, array or variable.
                	# Since you had some stuff about pType="string" etc, you could either base your choice on that
                	# or you use node names or use an array which helps you map node names to "other stuff", like I've
                	# previously shown in this thread.
                	# Or you can combine using pType if that exists, and some other method if it not always does.
                
                # So this is where you instantiate or create an empty array or define a variable.
                # You can assign directly to variables, and they will contain nothing but the nodeValue of current,
                # since they represent base types and should have nothing but one text node as child
                
                if ('basic_type')
                	# here you directly return $root->nodeValue
                
                # this was previously the part containing $xml = sprintf('....')
                else ('object or array')
                {
                	# Note: It may be cleaner to keep two if blocks in this block. one for objects, one for arrays
                
                	# But for objects and arrays, you need to instantiate an object or create an empty array.
                	#  ... which is done here...
                	foreach ($root->childNodes as $node)
                	{
                		# and then iterate over current node's child nodes and use a proper assignment technique
                		# depending on if it's object (member method) or array (array_push or [] operator), where
                		# the assigned value is obviously processTree($childNode)
                
                		# ... which is done here...
                	}
                
                	return $object_or_array;
                }
                } 		
                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_processed = processTree($list->item(0));
                
                var_dump($xml_processed); 
                
                  function objectToArray($d) {
                  	$d = is_object($d) ? get_object_vars($d) : $d;
                  	return is_array($d) ? array_map(__FUNCTION__, $d) : $d;	
                  }
                  
                    Write a Reply...