XML good practice

Hello all!

I’m working on a class that creates and works with xml files. After some trouble with namespaces and a bug in Flex that I managed to get around, this is what I’ve got so far (simplified)…


package uk.co.clockedin.test.test1{
    public class LibraryData
    {
        public namespace ns = "http://www.clockedin.co.uk/test1";
        private var xmlRoot:XML;

        private function constructNewXML():void
        {
            trace("Constructing new XML object.");
            use namespace ns;
            xmlRoot = 
            <xml xmlns='http://www.clockedin.co.uk/test1'> 
                <library />
                <counter nextID='1' />
            </xml>;
        }

        public function insertItem(item:LibraryItem):LibraryItem
        {
            use namespace ns;
            var newItem:XML = <item />;
            newItem.@id = getNextId();
            newItem.name[0] = item.name;
            newItem.description[0] = item.description;
            newItem.author[0] = item.author;
            libraryRoot.appendChild(newItem);
            return item;
        }
}
}

Despite many ambiguities and strange ways of having to go about things (default xml namespace doesn’t work in Flex for example), my main problem is with insertItem().

For some reason, my xml file is producing this…


  <xml xmlns="http://www.clockedin.co.uk/test1">
  <library>
  <item id="1">
  <aaa:name xmlns:aaa="uk.co.clockedin.test.test1:LibraryData">Sample Item</aaa:name> 
  <aaa:description xmlns:aaa="uk.co.clockedin.test.test1:LibraryData">Description of sample item.</aaa:description> 
  <aaa:author xmlns:aaa="uk.co.clockedin.test.test1:LibraryData">Timothy Jonathan</aaa:author> 
  </item>
  </library>
  <counter nextID="2" /> 
  </xml>

Is it just me, or does this seem very odd? If I specified at the beginning of the code block use namespace ns, why is it not doing so? And what’s all this aaa prefixing?
All I want is to add an item with the above-specified sub-nodes, and inherit the root node’s namespace, namely “http://www.clockedin.co.uk/test1”.
How can I do this and avoid this strange intrinsic namespacing? I’d prefer not to use a hack. I’m sure I’m not alone on this one…

Any suggestions?

use namespace isn’t for XMLs, it’s for the code itself…

var ns:Namespace = new Namespace("http://www.clockedin.co.uk/test1");
var xmlRoot:XML = 
		<xml> 
			<library />
			<counter nextID='1' />
		</xml>;
xmlRoot.setNamespace(ns);
trace(xmlRoot.toXMLString());

This is how you set nemespace for XML, if this was the question…

Hey, thanks for your reply, wvxvw.

I realise now that my use of use namespace isn’t strictly correct, but it does seem to work in xml too, and I’ve seen a few examples of it used elsewhere, like here.
My reason for using use namespace was as a replacement for the buggy default xml namespace in Flex. I didn’t want to have to specify the namespace in every line of xml code - what’s the point if I’m only working in 1 namespace anyway. Also, using use namespace hasn’t effected the execution of my code at all - shouldn’t it do that, if my code haven’t been declared in that namespace, or does use namespace do that for me?

My main issue, however, is this strange intrinsic adding of namespaces to the new nodes I created on a seperate XML object. All this stuff:

<aaa:name xmlns:aaa="uk.co.clockedin.test.test1:LibraryData">Sample Item</aaa:name>

Shouldn’t **use namespace **also fix this?

Sorry, lots of questions I know!

I’m going to continue to experiment and return here with my findings. Anyone wish to throw in some more idea and I’ll be very grateful, thanks again!

Any reason why this…


        private function constructNewXML():void
        {
            trace("Constructing new XML object.");
            var ns:Namespace = new Namespace("http://www.clockedin.co.uk/test/test1");
            xmlRoot = 
            <xml> 
                <library />
                <counter nextID='1' />
            </xml>;
            xmlRoot.addNamespace(ns);
        }

Is making this…


<xml>
<library />
<counter nextID="1" />
</xml> 

I’m writing the file using this…


            xmlStream.open(xmlFile, FileMode.WRITE);
            xmlStream.writeUTFBytes(xmlRoot.toXMLString());
            xmlStream.close();

What am I doing wrong? Am I going insane?

addNamespace() requires some prefix for the namespace, while setNamespace() doesn’t. i.e.:
first will produce xmlns:[someprefix]=“someURI” (only in case the provided namespace has prefix!).
second will produce xmlns=“someURI”;

Thanks dude, much appreciated - didn’t spot setNamespace(). Will implement it in the morning.

Getting to the original subject of my post ‘XML good practice’ I have a question about how to go about achieving a task in XML.

I wish to add an XML item to my library node. In AS2 I would’ve done this:

[LIST]
[]Created the ‘item’ node seperately.
[
]Populate it.
[*]Append it to the ‘library’ node - job done.
[/LIST]
In AS3, creating the ‘item’ seperately as an XML object gets all this namespace stuff added to it, so what’s the best way around this.

Perhaps appending a new node directly into the library xml would get around this. If so, what’s the best way of accessing this newly appended node in order to populate it. There’s no lastChild() method as in the AS2 XML class. Just wondering if there’s a nice quick way that I haven’t discovered yet.

What would everybody else do?

Cheers!

var xmllibrary:XML = <xmllibrary/>;
var xmlpattern:XML = <xmlpattern/>;

function addToLibrary(d:Object, i:int=1):void {
	var xn:XML = xmlpattern.copy();
	for (var entry:String in d) {
		xn.@[entry] = d[entry];
	}
	if(i <= 0) {
		xmllibrary.prependChild(xn);
	} else if (xmllibrary.*.length() > i ) {
		xmllibrary.replace(i, xn);
	} else {
		xmllibrary.appendChild(xn);
	}
}
var obj = {
	a:'foo',
	b:'bar'
};
var obj0 = {
	c:'123',
	d:'456'
};
var obj1 = {
	e:'XYZ',
	f:'OMG'
};
var obj2 = {
	g:'987',
	h:'@#$!'
};
addToLibrary(obj);
addToLibrary(obj0);
trace(xmllibrary.toXMLString());
trace('----------------------');
addToLibrary(obj1, 1);
trace(xmllibrary.toXMLString());
trace('----------------------');
addToLibrary(obj2, xmllibrary.*.length());
trace(xmllibrary.toXMLString());
trace('----------------------');
addToLibrary(obj0, 0);
trace(xmllibrary.toXMLString());

Something like this?

wvxvw, thanks!

I tried the code you posted and, as expected, it works. Here are the results of the final trace with the code as you wrote it (with slight changes to node names)…


<library xmlns="http://www.clockedin.co.uk/clients/maidstone">
  <item c="123" d="456"/>
  <item a="foo" b="bar"/>
  <item e="XYZ" f="OMG"/>
  <item g="987" h="@#$!"/>
</library>

Nice. The problem is, I wish to add sub nodes to the new ‘item’ nodes. It’s not good practice to only use attributes. So I changed the code to this, to create sub nodes…


		public function addToLibrary(d:Object, i:int = 1):void
		{
			var xn:XML = itemTemplate.copy();
		    for (var entry:String in d) {
		        xn[entry][0] = d[entry];
		    }
		    if(i <= 0) {
		        libraryRoot.prependChild(xn);
		    } else if (libraryRoot.*.length() > i ) {
		        libraryRoot.replace(i, xn);
		    } else {
		        libraryRoot.appendChild(xn);
		    }
		}

And I’m still getting this crazy namespace crap! Here’s the final trace…


<library xmlns="http://www.clockedin.co.uk/clients/maidstone">
  <item>
    <aaa:c xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">123</aaa:c>
    <aaa:d xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">456</aaa:d>
  </item>
  <item>
    <aaa:a xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">foo</aaa:a>
    <aaa:b xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">bar</aaa:b>
  </item>
  <item>
    <aaa:e xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">XYZ</aaa:e>
    <aaa:f xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">OMG</aaa:f>
  </item>
  <item>
    <aaa:g xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">987</aaa:g>
    <aaa:h xmlns:aaa="uk.co.clockedin.clients.maidstone:LibraryData">@#$!</aaa:h>
  </item>
</library>

I didn’t doctor the info this time - this is actually what I got. The xmlns:aaa entries all match the package:classname of the class I’m running.

One good thing I noticed, is that the toXMLString() automatically sets the xmlns of the <library> node the the inherited xmlns value in its parent node, so I must be on the right track…

Again, surely I’m not the only person who gets this. And surely there are more people out there who have overcome the problem.

Thanks again, wvxvw.

What I ended up doing was: set up the template first, like this…


private var itemTemplate:XML =
        <item>
            <title/>
            <subtitle/>
            <author/>
            <description/>
            <pubDate/>
        </item>;

And this kept the namespaces at bay when I changed the nodes like this…


            var newItem:XML = itemTemplate.copy();
            newItem.@id = getNextId();
            newItem.title[0] = item.title;
            newItem.subtitle[0] = item.subtitle;
            newItem.description[0] = item.description;
            newItem.author[0] = item.author;
            libraryRoot.appendChild(newItem);

Still, I’d like to know where this aaa namespace stuff comes from and why default xml namespace doesn’t fix it.

Look at the place, where you defined variables a,b,c,d … Does it happen to be somewhere after use namespace directive (while variables declaration has no namespace)? or did you declared them as aaa s:Object = {}; Or didn’t you specify any namespace for them at all? ( var s:Object = {}; )? and specified namespace for the function? In all those cases the namespace will be added to the variable’s name.