Welcome to WebDesignForums.net!
You're currently viewing WDF as a guest. By registering for a free account, you'll be able to participate with other members in our friendly community. Being a member allows you to ask questions and get answers for those troublesome web development tasks!

In addition, as a member you'll be able to post your websites up for review. Using our unique website review system you can gain some amazing feedback from some of the best web developers around. This is a completely free service to all registered members.

Ready to register yet? Registration is 100% free. Click Here To Join Now!

recursive XML parsing

Discussion in 'Adobe Flash Help' started by Tomaszewski, Mar 11, 2009.

  1. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    sooo... i have a question.. i'm tasked with building this app and need to take the below XML and parse it into a multi-dimensional associative array.

    Code:
    trace(_xmlDataArray["AL"][0])[INDENT]// 87369[/INDENT]
    trace(_xmlDataArray["AL"][1])[INDENT]// 12/31/2008[/INDENT]
    
    // etc....
    
    

    Code:
    	<state>
    		[INDENT]<name>AL</name>
    		<income>87369</income>
    		<doctorStats>
    			[INDENT]<period id="1">
    				[INDENT]<date>12/31/2008</date>
    				<participants>485</participants>[/INDENT]
    			</period>
    			<period id="2">
    				<date>12/31/2003</date>
    				<participants>294</participants>
    			</period>
    			<period id="3">
    				<date>12/31/1998</date>
    				<participants>211</participants>
    			</period>[/INDENT]
    		</doctorStats>
    		<prescription>
    			[INDENT]<type>Non-controlled drugs from approved formulary</type>[/INDENT]
    		</prescription>
    		<programs>
    			[INDENT]<prog id="1">
    				[INDENT]<name>University of South Alabama in Mobile</name>
    				<link>http://southalbama.edu/alliedhealth/pa/</link>[/INDENT]
    			</prog>
    			<prog id="2">
    				[INDENT]<name>University of Alabama at Birmingham Surgical</name>
    				<link>http://main.uab.edu/shrp/default.aspx?pid=77392</link>[/INDENT]
    			</prog>
    			<prog id="3">
    				[INDENT]<name></name>
    				<link></link>[/INDENT]
    			</prog>[/INDENT]
    		</programs>
    		<organizations>
    			[INDENT]<org>
    				[INDENT]<type>State Organization</type>
    				<name>Alabama Society of Physician Assitants</name>
    				<link>http://www.aapacoms.org/aspa</link>[/INDENT]
    			</org>
    			<org >
    				[INDENT]<type>Federal Organization</type>
    				<name>Federal Society of Physician Assitants</name>
    				<link>http://www.thefeds.gov</link>[/INDENT]
    			</org>
    			<org >
    				[INDENT]<type></type>
    				<name></name>
    				<link></link>[/NDENT]
    			</org>[/INDENT]
    		</organizations>
    		<fact>Did you know? About 46.3% of Alabama PA's practice in a surgical specialty</fact>
    		<source>Source: American Academy of Physicians Assistants</source>[/INDENT]
    	</state>
    
    
    The question I have is how in the heck do I get is so that each text value of each node gets shoved into the appropriate array index?

    So each state will have a <state>PA</state> XML structure with its own data just like AL above in the XML. So for (i.e., PA), i need to put the relevant data like income into index [0] of the _xmlDataArray["PA"] array.

    Here is what I have so far:

    Code:
    
    
    
    		private function buildArray(theXML:XML):void
    		{
    			_xmlDataArray = new Array();
    
    			for (var i:uint; i < theXML.state.length(); i++)
    			{
    				//trace(theXML.state.name[i]);
    				_xmlDataArray[theXML.state[i].name.children()] = [theXML.state[i].income.toString()];
    				recurssion(theXML.state[i].name,theXML);
    			}
    		}
    		
    		private function recurssion(usaState:String, theXML:XML):void
    		{
    			if(theXML.children())
    			{
    				//for each
    			}
    			trace(_xmlDataArray[usaState]);
    		}
    
    
    I've attached the XML file beacuse the rendering on this post isn't the best!

    Attached Files:



  2. Offline

    smoseley Administrator

    Message Count:
    9,727
    Likes Received:
    192
    Trophy Points:
    63
    Location:
    Boston, MA
    Try something like this.. Not fully tested, but you see where it's going:

    Code:
    var arr:Array = convertXmlToArray(xml);
    
    function convertXmlToArray(xml:XML):Array {
    	var xmlArray:Array = new Array();
    	
    	// Populate children
    	for (var i:uint = 0; i < xml.length(); i++) {
    		var xmlNode:XML = xml.children()[i];
    		if (xmlNode.hasComplexContent()) {
    			// Recursion
    			xmlArray[xmlNode.name().localName] = convertXmlToArray(xmlNode);
    		}  else {
    			// Text node
    			xmlArray[xmlNode.name().localName] = xmlNode.toString();
    		}
    	}
    	
    	// Return appropriate element
    	if (xml.parent() == null) {
    		// Top level element
    		var mainArray:Array = new Array();
    		mainArray[xml.name().localName] = xmlArray;
    		return mainArray;
    	} else {
    		// Other element
    		return xmlArray;
    	}
    }
    


  3. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA

    Ok, but this traces state where it needs to be the name of the state (i.e., "AL"). So i converted it to

    Code:
    _xmlDataArray[xmlNode.child("name")] = "blah blah";
    
    ... this works...

    Code:
    
    trace(_xmlDataArray["AL"]);[INDENT]//blah blah[/INDENT]
    
    

    still, i can't figure out a way how to populate the array with the rest of the data


  4. Offline

    smoseley Administrator

    Message Count:
    9,727
    Likes Received:
    192
    Trophy Points:
    63
    Location:
    Boston, MA
    Ok, I see how you want it now.

    On a side note, I forgot that ActionScript Arrays don't allow associative keys. You'll have to use the Object Class.

    This should do what you want (you can still reference the object values as though they are part of an array):

    Code:
    var xml:XML = 
    	<medicalInfo>
    		<state>
    			<name>AL</name>
    			<source>Source: American Academy of Physicians Assistants</source>
    		</state>
    		<state>
    			<name>GA</name>
    			<source>Source: Other Stuff</source>
    			<complex>
    				<val1>test</val1>
    				<val2>another test</val2>
    			</complex>
    		</state>
    	</medicalInfo>;
    	
    
    var medInfo:Object = getStateArray(xml);
    
    trace(medInfo);
    trace(medInfo["AL"]["source"]);
    trace(medInfo.AL.source);
    trace(medInfo.GA.complex.val1);
    trace(medInfo.GA.complex.val2);
    
    function getStateArray(xml:XML):Object {
    	var xmlAssoc:Object = new Object();
    	for (var i:uint = 0; i < xml.children().length(); i++) {
    		var xmlNode:XML = xml.children()[i];
    		xmlAssoc[xmlNode.child("name")] = convertXmlToAssoc(xmlNode);
    	}
    	return xmlAssoc;
    }
    
    
    function convertXmlToAssoc(xml:XML):Object {
    	var xmlAssoc:Object = new Object();
    	
    	// Populate children
    	for (var i:uint = 0; i < xml.children().length(); i++) {
    		var xmlNode:XML = xml.children()[i];
    		if (xmlNode.hasComplexContent()) {
    			// Recursion
    			xmlAssoc[xmlNode.name().localName] = convertXmlToAssoc(xmlNode);
    		}  else {
    			// Text node
    			xmlAssoc[xmlNode.name().localName] = xmlNode.toString();
    		}
    	}
    	return xmlAssoc;
    }
    


  5. Offline

    Shadowfiend Code beautifully and honorably

    Message Count:
    4,146
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Atlanta, GA
    To be clear, Array does support associative keys, but if you're going to use them there's no reason to use Array. That is to say, since an Array is an Object, it will also take associative keys, but then you've needlessly added the functionality of an Array to something that need only be an Object.


  6. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    I feel stupid!

    ME:
    [IMG]

    Your a genious. Coulple of question though (surprised?? LOL):

    I rewrote your code to learn what its doing. I also integrated as part of my class.

    1. After reading Adobe's description of xml.hasComplexContent() property, I must say that I'm lost as to why you have used it. The only thing I can think of is that if its complex content, than it gets treated like an XMLList and not XML. But then again... if so... than I didn't see you calling anything from the XMLlist.
    2. The same goes for localName. From what I can tell, this is outputting the name contained by the XML (i.e, <name>AL</name> // AL)...
    3. In my XML tree, there are 3 <period> children under <doctorStats> as an example. Currently, the recursive code is pulling in only 1 of those. So if I wanted to pull into the array, as many as there are, would that mean I would have to do a for loop if the recursion sees children()?


  7. Offline

    Shadowfiend Code beautifully and honorably

    Message Count:
    4,146
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Atlanta, GA
    hasComplexContent basically returns true if the element has children.

    localName is used to retrieve the name of the XML element without a namespace (in your case, there are no XML namespaces anyway, so it would be the same as the name).

    Your problem with the three period children is that you would have to check for multiple children of the *same name*, and, instead of assigning them to the same spot in the object, you would need an array to store them. So the way it's currently doing it now, it tries to put the first period object into arr["period"], and then when it sees the next one, it puts it in the same place, replacing the other one. One good way to do it would be this:

    Code:
                    var content:Object;
    
                    if (xmlNode.hasComplexContent())
                    {
    			// Recursion
    			content = convertXmlToAssoc(xmlNode);
    		}
                    else
                    {
    			// Text node
    			content = xmlNode.toString();
    		}
    
                   var name:String = xmlNode.name().localName;
                   var currentElement:Object = xmlAssoc[name];
                   if (! currentElement) // if there is nothing there yet
                       xmlAssoc[name] = content;
                   else if (currentElement is Array)
                       currentElement.push(content);
                   else
                       xmlAssoc[name] = new Array(currentElement, content);
    
    This basically builds the data for the current child, then checks if there is an existing entry in the list for it. If there is, then it makes sure that entry is an array and adds the content of this child to that array.


  8. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    can you elaborate on this a little more?

    so, would I put this after the first recursion?

    example:

    Code:
    		private function buildArray(xml:XML):Object
    		{
    			var xmlAssoc:Object = new Object();
    			for (var i:uint = 0; i < xml.children().length(); i++)
    			{
    				var xmlNode:XML = xml.children()[i];
    				xmlAssoc[xmlNode.child("name")] = recursion(xmlNode);
    			}
    			
    			return xmlAssoc;
    		}
    		
    		private function recursion(xml:XML):Object
    		{
    			var xmlAssoc:Object = new Object();
    			for (var i:uint = 0; i < xml.children().length(); i++)
    			{
    				var xmlNode:XML = xml.children()[i];
    				if (xmlNode.hasComplexContent())
    				{
    					//Recurssion
    					xmlAssoc[xmlNode.name().localName] = recursion(xmlNode);\
    [INDENT][COLOR="Red"][B]/* ******************* HERE ******************************** /[/B][/COLOR][/INDENT]
    				}
    				else
    				{
    					xmlAssoc[xmlNode.name().localName] = xmlNode.toString();
    				}
    			}
    
    


  9. Offline

    Shadowfiend Code beautifully and honorably

    Message Count:
    4,146
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Atlanta, GA
    You would replace the body of the for loop, except for the xmlNode declaration, with it.

    As for namespaces. Basically, an XML namespace is a way to include tags from multiple `categories' in a single document. For example, if I wanted to use regular XHTML with FBML (the Facebook Markup Language) in a browser that understood both, I would have an xmlns for the HTML part and one for the fbml part. This means that I could use tags from both categories in my document.

    Namespaces are usually of the syntax [minicode]namespace:tagname[/minicode]. In your case, as mentioned, you don't actually have a namespace, so name and localName would likely be the same. Namespaces are used a lot in things like the XMPP protocol and Flex MXML.


  10. Offline

    smoseley Administrator

    Message Count:
    9,727
    Likes Received:
    192
    Trophy Points:
    63
    Location:
    Boston, MA
    Expanding on Shadowfiend's above explanation, here's an example of XML namespaces in use:

    Code:
    <?xml version="1.0"?>
    <[B]myXmlDoc [/B]xmlns="http://mynamespace.mydomain.com/subnamespace" 
            xmlns:steve="http://stevesnamespace.stevesdomain.com/subnamespace">
    
        <[B]myElement[/B]>
            <[B]name[/B]>This is an element in my namespace</[B]name[/B]>
            <[B]description[/B]>Notice the generic 'name' and 'description' children have no namespace
                defined.  This is because they are part of the default namespace.</[B]description[/B]>
        </[B]myElement[/B]>
    
        <[B]myOtherElement[/B]>This is another element in my namespace</[B]myOtherElement[/B]>
    
        <[B]steve:stevesElement[/B]>
            <[B]steve:name[/B]>This is one of Steve's elements</[B]steve:name[/B]>
            <[B]steve:description[/B]>Notice all of the defining parts of the 'steve' XML element are tagged
                with the 'steve' namespace.  This allows us to selectively parse overlapping elements
                of different schemas using the namespace prefix to separate them.</[B]steve:description[/B]>
        </[B]steve:stevesElement[/B]>
    
    </[B]myXmlDoc[/B]>
    
    Neat, no? :)


  11. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA

    Indeed... and now I understand. As for the code, I got it working thanks to the both of you. Here is the code:

    http://friendpaste.com/5h5B416xemqg2zO6UQxVLV

    I do have one additional question to this. We are using Objects here to keep the data in a tree like structure. Lets say however, that I wanted to slap all the data and flatten it into an array so that I can call the data like this: _xmlDataArray["PA"][4]... I would have to than push() the data into the Array no?


  12. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    so is there a way to add the data not into an Object but into a flat array instead? Like [0], [1], [2]... etc... i've been trying to get it to work, but am not having much like. I'm convinced that the line:

    Code:
     xmlAssoc[name] = new Array(currentElement, tempObj); 
    is what is creating the tree like object, so that instead of having just the values in the array, i have

    Object
    |
    - Project
    |
    - "value"
    - "value"

    i'd rather have

    Array
    |
    [0] - "value"
    [1] - "value"
    [2] - "value"


    know what i mean?


  13. Offline

    Shadowfiend Code beautifully and honorably

    Message Count:
    4,146
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Atlanta, GA
    Not 100% clear on it, no, but I think that a variant of the array solution I showed above should work.


  14. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    Ok, thusfar I've gotten this far: http://friendpaste.com/339gSTqjhOV4s1egg4oYhu

    ... but am seriously stuck... the array _xmlDataArray spits out:

    _xmlDataArray["AL"]

    [0] > 1234
    [1] > 23525
    [3] - [0] > sdfsdf
    - - - -[1] > 234234
    - - - -[2] > 234234
    [4] > 234sdfsdf
    [5] > 23hsfsf
    - - - -[0] sdf222
    - - - -[1]234234
    [6] sdfsdf


    ... and I can't flatten the rest of it... like [3][0], [3][1], [3][2], etc. :ichatcry:


    ... and i think its because line 13 gets a return of an array. So i tried making it get a String, but then the state abbreviation gets messed up. :(


  15. Offline

    Tomaszewski New Member

    Message Count:
    194
    Likes Received:
    0
    Trophy Points:
    0
    Location:
    Exton, PA
    Let me ask another way... if I wanted to spit out the contents of _xmlDataArray... and if the array has multiple items inside multiple items... (i.e., [0][1], [0][2], [0][3]...etc)... how can I get it to display all those items on individual lines on the screen?


    Currently I'm using:

    Code:
    			for each(var key:String in _xmlDataArray["AL"])
    			{
    				trace(key + "\n");
    			}
    
    but it just gives me the first indexes items, but not any other index inside... know what i mean?

    for example

    Code:
    AL
    
    87369
    
    12/31/2008,485,12/31/2003,294,12/31/1998,211
    
    Non-controlled drugs from approved formulary
    
    University of South Alabama in Mobile,http://southalbama.edu/alliedhealth/pa/,University of Alabama at Birmingham Surgical,http://main.uab.edu/shrp/default.aspx?pid=77392
    
    State Organization,Alabama Society of Physician Assitants,http://www.aapacoms.org/aspa,Federal Organization,Federal Society of Physician Assitants,http://www.thefeds.gov
    
    Did you know? About 46.3% of Alabama PA's practice in a surgical specialty
    
    Source: American Academy of Physicians Assistants
    
    
    but if you notice the dates

    Code:
    12/31/2008,485,12/31/2003,294,12/31/1998,211
    they are not getting displayed on a new line... grr...


Share This Page