Down Load: Source Code
--Load the xml assembly dotNet.loadAssembly "system.xml" --Create an xml document object. xmlDoc=dotNetObject "system.xml.xmlDocument"
First we will set the path to the XML file that we wrote out in the previous tutorial and store it in xmlPath. Next I'm just doing some error checking to confirm that the file does exist.
If you do a "showMethods xmlDoc" one of the methods is .load. This method will load an XML doc in several different ways. The one that we need is loading from a file.
Every XML file has to have one root element, you can access that element using the .documentElement property. We will set a variable for that called docEle. I usually confirm that the documentElement exists and then I test the name property of docEle. The name property of the documentElement will return the name that we gave it. In this case it should return "Root", I also test this to make sure that I have the right xml file loaded. Usually I will use a more descriptive documentElement name. In the case of penHelper I use the name penObjects since all of my penObjects can use the same format.
Once the documentElement is confirmed I am using "showProperties xmlEle" and then I'm doing the same to to of it's properties. '.attributes' and '.childNodes'. '.attributes' will return a dotNet object that contains all the attributes on a given element, the object if printed will look like this..
dotNetObject:System.Xml.XmlAttributeCollectionand has all of it's own properties and methods. We will use that later on. '.childNodes' returns an array of child elements of documentElement, we will use this property to recurse down the hierarchy to collect all the elements and their contents.
--Open the file in Max to see the file. xmlPath=((getDir #scripts)+"\\test.xml") --If the file exists then load it. if doesFileExist xmlPath then ( --Load the XML file. xmlDoc.load xmlPath --Check to make sure the xmlDoc has a root element. docEle=xmlDoc.documentElement --If the root element exists continue. if docEle!=undefined and docEle.name=="Root" then ( clearListener() format "Element Name: %\n\n" docEle.name format "docEle Props:\n" showProperties docEle format "\nAttribute Props:\n" showProperties docEle.Attributes format "\nChildNodes Props:\n" showProperties docEle.ChildNodes ) )
fn recurseXml ele= ( for i = 0 to ele.childNodes.count-1 do ( print ele.ChildNodes.itemOf[i].name recurseXml ele.ChildNodes.itemOf[i] ) )
I have changed up the if statement below from showing the properties and methods to calling the recurseXml function. First is create the xmlDoc and then checks to see if it has a root element called "Root: then calls the recursion.
--If the file exists then load it. if doesFileExist xmlPath then ( --Load the XML file. xmlDoc.load xmlPath --Check to make sure the xmlDoc has a root element. docEle=xmlDoc.documentElement --If the root element exists continue. if docEle!=undefined and docEle.name=="Root" then ( --Recurse the XML tree. recurseXml docEle ) )
fn recurseXml ele= ( for i = 0 to ele.childNodes.count-1 do ( --Loop through all the attributes in an element. for ii = 0 to ele.ChildNodes.itemOf[i].attributes.count-1 do ( --First method --Get the name of the attribute attribName=ele.ChildNodes.itemOf[i].attributes.itemOf[ii].name --Get the value of the attribute as a string. attribVal=ele.ChildNodes.itemOf[i].attributes.itemOf[ii].value --Second method. --You can also get the attribute by name and then get the value from it. attrib=ele.ChildNodes.itemOf[i].attributes.getNamedItem attribName --Format the result to the listener format "AttribName: % Value: %\n" attrib.name attrib.value ) recurseXml ele.ChildNodes.itemOf[i] ) )
You can also get to the attributes without the .attributes property if you know the name of it using the getAttributeNode method and passing it the name of the attribute and then accessing the value property.
(ele.ChildNodes.itemOf[i].GetAttributeNode "class").value
"class" being the name of the attribute that we want the value of. The only values that are returned are string so we will have to use a function like execute if we want to get values or other classes of max attributes.
We have added another parameter to the function called parent. This will be the last node created and will set as the parent of the next.
Once we have the values of the two attributes that we stored with each node we will execute the class attribute string and then check if it is a creatable class. If it is creatable then we create it, name it and then set the parent property. When the recurseXML function is called again we need to pass the node that was created to the parent property so that it can be set on the next object.
fn recurseXml ele parent:undefined= ( --Loop through all the children for i = 0 to ele.childNodes.count-1 do ( --Get the attribute value called class objClassStr=(ele.ChildNodes.itemOf[i].GetAttributeNode "class").value --Get the attribute value called name objNameStr=(ele.ChildNodes.itemOf[i].GetAttributeNode "name").value --Check if the class value can be turned into a max class object and is creatable. objClass=(execute objClassStr) if objClass!=undefined and objClass.creatable then ( --Create the object n=objClass() --Name the object n.name=objNameStr --Set the parent of the object if there is one. n.parent=parent ) --recurse the xml tree recurseXml ele.ChildNodes.itemOf[i] parent:n ) )
Some of the data that is missing form this example are position, properties of the objects like radius of a sphere or the height of a box. You could also take into consideration UV coordinates, materials and controllers on the object.