Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Adobe AIR Development

Reply
Developer
Developer
Posts: 1,123
Registered: ‎02-10-2009
My Device: 8130 / 8350 / 9530 / 9550 / 9850 / PlayBook
My Carrier: Verizon
Accepted Solution

XML parsing

[ Edited ]

Im having a hard time creating/updating an XML object. Whenever I add a child node using the XML.appendChild() function it is not adding a node but an attribute and so when I try to search for the child it is not finding it and thus places every node under the top level as an attribute.

 

Here is the code I am using:

 

 

      public function addKey(key:String, value:String):void {
         //break key into individual strings
         var children:Array = key.split('.');
                 
         //variable to hold the node that corresponds to key
         var finalKey:XML;
                 
         //using the array created by key.split to climb down the XML object
         for (var i:uint = 0; i < children.length; ++i) {
            if (i == 0) {
               trace("First child in array: "+children[0]);
               if( _workingCopy.child(children[i]).length() == 0 ){
                  trace("Element: "+children[i]+" is not contained in XML document......adding");
                  _workingCopy.appendChild(children[i]);
                  finalKey = _workingCopy.child(children[i])[0];
               } else {
                  trace("Element: "+children[i]+" is contained in XML document......retrieving");
                  finalKey = _workingCopy.child(children[i])[0];
                  trace("Element: "+children[i]+" has "+finalKey.children().length()+" children");
               }
            } else {
               if( finalKey.child(children[i]).length() == 0 ){
                  trace("Element: "+children[i]+" is not contained in "+finalKey.name()+" document......adding");
                  finalKey.appendChild(children[i]);

trace( finalKey.toXMLString() );
                  trace( _workingCopy.toXMLString() );
 
finalKey = finalKey.child(children[i])[0]; } else { trace("Element: "+children[i]+" is contained in "+finalKey.name()+" document......retrieving"); finalKey = finalKey.child(children[i])[0]; } } } trace(); //finalKey.@value = value; }

 

 

Here is the data I am calling it with and my inital file structure:

 

 

      private var _settingsTemplate:XML = new XML("<data>"+
                                                  "<events>0</events>"+
                                                  "<current>0</current>"+
                                                  "<currentEvent>"+
                                                  "</currentEvent>"+
                                                  "<event>"+
                                                  "</event>"+
                                                  "</data>");



         trace( _workingCopy.toXMLString() );
         
         addKey("event.1.type","test");
         addKey("event.2.name","test");
         addKey("event.2.type","test");
         addKey("event.3.name","test");
         addKey("currentEvent.name","updated sample event");
         
         trace( _workingCopy.toXMLString() );

 

Here is the output i get:

 

 

<data>
  <events>0</events>
  <current>0</current>
  <currentEvent/>
  <event/>
</data>

First child in array: event
Element: event is contained in XML document......retrieving
Element: event has 0 children
Element: 1 is not contained in event document......adding

<event>1</event>

<data>
  <events>0</events>
  <current>0</current>
  <currentEvent/>
  <event>1</event>
</data>
TypeError: Error #1009: Cannot access a property or method of a null object reference.

 

 

I read that appendChild adds the node to the end of the children's list, but that does not seem to be what's happening. Also, I cannot add nodes to the the top level data node, it always adds the node to the last child node. Even if I wrap the children[] object with XML() it still has the same effect.

 

 

Developer
Posts: 278
Registered: ‎10-27-2010
My Device: Playbook

Re: XML parsing

During my initial tests I am slightly confused by this variable and where it comes from: _workingCopy

 

I am sure you are creating and setting it somewhere, but that is outside of your code example at this point.

 

I am hoping to draft up something that might help you out.

--------------
kdittyr

Accepted PlayBook Applications:
HDB Converter -- Utilities/Calculators
Developer
Developer
Posts: 1,123
Registered: ‎02-10-2009
My Device: 8130 / 8350 / 9530 / 9550 / 9850 / PlayBook
My Carrier: Verizon

Re: XML parsing

For all intensive purposes you can set _workingCopy with this code:

 

 

_workingCopy = _settingsTemplate.copy();

 

 

Developer
Posts: 278
Registered: ‎10-27-2010
My Device: Playbook

Re: XML parsing

[ Edited ]

OK, CMY, what is your expected output?

 

Thanks for the clarification on that variable...  it is what I figured, but I didn't want to make assumptions.

 

Maybe I am a little confused, but wouldn't you be better off using something like this?

 

It is a lot more readable, in my opinion.

 

 

private var _settingsTemplate:XML = new XML( <data>
	<events>0</events>
	<current>0</current>
	<currentEvent />
	<event />
	</data> );

private var _workingCopy:XML = _settingsTemplate.copy();

public function addKey( _xml_node:XML ):void
{
	_workingCopy.appendChild( _xml_node );
}

public function updateNode( _xml_node:String, _replace_with:XML ):void
{
	_workingCopy.replace( _xml_node, _replace_with );
}

 

Just tracing out the _workingCopy first will produce the following output:

 

trace();
trace( 'Before xml changes' );
trace( _workingCopy.toXMLString() );

// outputs:
Before xml changes
<data>
  <events>0</events>
  <current>0</current>
  <currentEvent/>
  <event/>
</data>
*********************/

 

Add a bunch of new keys and update a node, then trace out the _workingCopy to produce the following output:

 

 

addKey( XML( <event type="test" /> ) );
addKey( XML( <event name="test" type="test" /> ) );
addKey( XML( <event name="test" /> ) );
updateNode( 'currentEvent', XML( <currentEvent name="updated sample event" /> ) );

trace();
trace( 'After xml changes:' );
trace( _workingCopy.toXMLString() );

// outputs:
/*********************
After xml changes:
<data>
  <events>0</events>
  <current>0</current>
  <currentEvent name="updated sample event"/>
  <event/>
  <event type="test"/>
  <event name="test" type="test"/>
  <event name="test"/>
</data>
*********************/
--------------
kdittyr

Accepted PlayBook Applications:
HDB Converter -- Utilities/Calculators
Developer
Posts: 65
Registered: ‎11-15-2010
My Device: Not Specified
My Carrier: T-Mobile

Re: XML parsing

In checking out this portion of the code

 

//using the array created by key.split to climb down the XML object
         for (var i:uint = 0; i < children.length; ++i) {

 

You're not "climbing down the XML object" you're just looping over the "key" that is passed it to addKey.  So it's looping over event.1.type not _workingCopy 's children, right?

 

Developer
Developer
Posts: 1,123
Registered: ‎02-10-2009
My Device: 8130 / 8350 / 9530 / 9550 / 9850 / PlayBook
My Carrier: Verizon

Re: XML parsing

@kdittyr

 

That would be easier to do and it works, which i guess is all that matters, but it still doesn't explain why the issue was occuring. Also, the data to be added is entered by the user, so the input string has to be built which would still not work as appendChild is not creating a node and if I manually type the < or > they are trated as part of the string and converted.

 

ie:

 

 

//name
finalKey.appendChild('<'+children[i]+'>');
finalKey.appendChild("<"+children[i]+">");

 

 

gives

 

 

&lt;name&gt;
&lt;name&gt;

 

 

@lampei

 

Yes it is looping over the string that is passed in to create nodes and insert them in to the XML tree

Highlighted
Developer
Posts: 278
Registered: ‎10-27-2010
My Device: Playbook

Re: XML parsing

[ Edited ]

You need to type cast as XML and you won't have that output error ( &lt;name&gt; ):

 

finalKey.appendChild( XML( '<' + children[ i ] + '>' ) );
finalKey.appendChild( XML( "<" + children[ i ] + ">" ) );

As for why you are getting the original error...  well, you need to subtract one from the length, otherwise you are looking for something that is actually a null object, or out of range.

 

for( var i:uint = 0; i < children.length; ++i )

should be:

var _length:int = children.length - 1; // removing "extra" loop

for( var i:uint = 0; i < _length; ++i )

Because we use zero indexing in software development the first element is in the 0 position.

 

If there were 5 children, the last index would actually be 4, not 5.  Make sense?

 

I am not sure that this is creating the output that you are expecting, though.  Would you mind posting a sample of what you are trying to create?  This is now outputting:

 

<data>
  <events>0</events>
  <current>0</current>
  <currentEvent/>
  <event>
    1
    2
    2
    3
  </event>
</data>
--------------
kdittyr

Accepted PlayBook Applications:
HDB Converter -- Utilities/Calculators
Developer
Developer
Posts: 1,123
Registered: ‎02-10-2009
My Device: 8130 / 8350 / 9530 / 9550 / 9850 / PlayBook
My Carrier: Verizon

Re: XML parsing

[ Edited ]

Wrapping it in the XML tag with the included < > solved the problem of creating the node. I also found a workaround to getting the child, when initially added it does not show up under children, but it does show up under the elements list:

 

 

//finalKey = finalKey.child(children[i])[0];   //Does not work
finalKey = finalKey.elements(children[i])[0];  //Works

 

And upon each subsequent search of children the nodes popup. Thanks for you assistance.

 

 

The error about the null object was not due to a looping error it was because the child node was not being added correctly. Before it was not creating a node at all, and after I correctly created the node, it was not populating in the child list, but using the elements list fixed that. I don't know if it's a bug or not, but it's working.

Developer
Posts: 278
Registered: ‎10-27-2010
My Device: Playbook

Re: XML parsing

No problem Smiley Happy  Glad I could help out in some way!

--------------
kdittyr

Accepted PlayBook Applications:
HDB Converter -- Utilities/Calculators
Developer
Posts: 165
Registered: ‎10-26-2010
My Device: BlackBerry® PlayBook™
My Carrier: .

Re: XML parsing

Hi CMY--

 

Glad to see you got this working and thanks for posting your final solution. It looks like you used the settings manager class I posted a while back as a starting point. When I posted it , I hadn't had a need for addKey yet, but I did add a version of it in the last update to one of my desktop apps. It uses appendChild like you were originally trying to do. Here it is for future reference, if you're interested:

 

 

public function addKey(key:String, label:String, value:String):void
{
	var children:Array = key.split('.');
	var finalKey:XML = new XML();
	var insert:XML = <{children[children.length - 1]} label={label} value={value}></{children[children.length - 1]}>;
	
	for (var i:uint = 0; i < children.length - 1; ++i)
	{
		if (i == 0)
		{
			finalKey = XML(newSettings.child(children[i])[0]);
		}
		else
		{
			finalKey = XML(finalKey.child(children[i])[0]);
		}
	}
			
	finalKey.appendChild(insert);
}

One thing I haven't done yet, but have it on the roadmap, is to encapsulate the for loop that finds the final key into a function so it isn't repeated everywhere.

 

Chris