Using E4X with XML in AS3
Posted by: Keith H in ActionScript 3, XML, tags: E4X Filtering, Regular Expressions, XMLThe XML examples represent a book in a library. However the book has several bookworms in it.
Using E4X we can find these bookworms.
<library> <book> <page id="one"/> <worm name="Eddy" /> <page id="two"/> <page id="three"> <worm name="Lisa"/> <worm name="Pete" /> </page> <page id="four"/> </book> </library>
There are some E4X operators and XML methods to help use do this when combined together:
.. Access all descendants of the XML object.
. Access a property of an Object or XML.
* Wildcard, match any part of the XML.
childIndex() Index number of an XML object relative to its parent node.
name() Name of the XML node.
Return a certain "worm" node at the index it belongs to from its XMLList.
Any nested "worm" node occurrence is also considered.
var doc:XML= <library> <book> <page id="one"/> <worm name="Eddy" /> <page id="two"/> <page id="three"> <worm name="Lisa"/> <worm name="Pete" /> </page> <page id="four"/> </book> </library>; //Pick the "worm" node at index 1. var data:*=doc..*.worm[1]; trace(data.toXMLString()); /*========================================= OUTPUT: <worm name="Lisa"/> //worm[1] ===========================================*/
Delete all "worm" nodes from the xml, even the nested ones.
var doc:XML= <library> <book> <page id="one"/> <worm name="Eddy" /> <page id="two"/> <page id="three"> <worm name="Lisa"/> <worm name="Pete" /> </page> <page id="four"/> </book> </library>; delete doc..*.worm; trace(doc.toXMLString()); /*========================================= OUTPUT: All "worm" nodes are deleted. <library> <book> <page id="one"/> <page id="two"/> <page id="three"/> <page id="four"/> </book> </library> ===========================================*/
Use E4X filtering with Regular Expressions.
XML methods and Regular Expressions can be used inside parentheses to accomplish E4X filtering.
doc..*.(childIndex()==0 && /library|book/i.test(name())==false)
List all nodes that belong at a given index of their XMLList.
var doc:XML= <library> <book> <page id="one"/> <worm name="Eddy" /> <page id="two"/> <page id="three"> <worm name="Lisa"/> <worm name="Pete" /> </page> <page id="four"/> </book> </library>; //---------------------------------------------------- //Get the first node of each list whose name does not //match the regular expression. //---------------------------------------------------- var data:*=doc..*.(childIndex()==0 && /library|book/i.test(name())==false); trace(data.toXMLString()); /*========================================= OUTPUT: <page id="one"/> <worm name="Lisa"/> ===========================================*/
Entries (RSS)
re: delete doc..*.worm;
So if the worm nodes had another attribute – say type, with ‘good’ or ‘evil’, and say 2 were ‘evil’ – how would you filter the xml to delete just the evil 2?
You would think:
delete doc..*.worm.(@type==”evil”);
but you get the error “Delete operator is not supported with operand of type XMLList” – strange as you’d assume doc..*.worm is returning an XMLList too…
You can go
doc..*.worm.(@type==”evil”)[0];
which would delete one of them (and naturally [1] to delete the other – but clearly not helpful) – so does that mean looping over the XMLList is the only way to delete multiple nodes based on a filter criteria? Be a bit of a shame if that is the case – hopefully you can tell me I’m missing something obvious??
I’d like more info on it too.
I think it’s safe to say you can’t delete an E4X expression operator “()”, but you can delete an XML node of it.
and all of the nodes nested in that node….by using [index] bracket operator on it
Yep its strange. The return datatype of this syntax “doc..*.worm” is an XMLList.
doc..*.worm //technically this is XMLList but delete works when the E4X syntax DOES NOT have an expression.
doc..*.(name()==”worm”) //The return type is XMLList and delete does not work with XMLList.
doc..*.(name()==”worm”)[0] //The return type is XML and delete works for XML.
The “Expression” part of E4X syntax always returns an XMLList.
The delete operator does not work with XMLList.
delete doc..*.(name()==”worm”);//TypeError: Error #1119: Delete operator is not supported
More strange confusion when I test:
trace(“Testing ‘typeof’”);
trace(typeof doc..*.worm); //says its XML an not XMLList
trace(typeof doc..*.(name()==”worm”)); //also says its XML an not XMLList
trace(“”);
trace(“Testing ‘is’”);
trace(doc..*.worm is XML);
trace(doc..*.(name()==”worm”) is XML);
trace(“”);
Yeah it doesn’t really seem too logical does it? Just think that I must be missing some trick here, but there you go!
I think typeof will return ‘XML’ no matter if it’s XML or XMLList every time – you know like typeof on an Array is always ‘object’, but it is interesting that using ‘is’ gives false for is XML, true for is XMLList – yet you can delete (i.e. no error #1119) when the XMLList wasn’t made using a filter. Really weird.
Ah well, looping it is! For now…
You’re awesome. That “[" "]” solution for the warning of RegExp .test fixed my warnings. Thanks!
Dom
thanx this has been very helpful. I do have one question I cant seem to find an answer to. Say I have an xml
good book
some dude
badbook
bill man
soso book
jane dow
and I have want to find an item in that xml
var bookList:XMLList = bookInput.Book.(author == “some dude” );
trace(bookList);
easy enough but now I need to know what the index value is of that node so for example I can delete it or something else. How do I get that ??
thanx
Dave, Looks like the characters were cut off,
anyway if “author” is an attribute, you can do the following…
var bookList:XMLList = bookInput.Book.(attribute(“author”) == “some dude” );//returns an XMLList
var item:XML=bookList[0]; //Is this the actual child XML or “item” you are looking for?
//If need to delete it…
delete bookList[0];
(trying this again escaping xml)
This still has me so freakin’ confused.
Convert your xml doc to the following, which is valid xml (no surrounding book node):
var doc:XML=
<library>
<page id=”one”/>
<worm name=”Eddy” />
<page id=”two”/>
<page id=”three”>
<worm name=”Lisa”/>
<worm name=”Pete” />
</page>
<page id=”four”/>
<worm name=”John”/>
<worm name=”bill”>
<worm name=”Sally”/>
<worm name=”Fred”/>
</worm>
</library>;
Now after you do:
delete doc..*.worm;
you end up with:
doc =<library>
<page id=”one”/>
<worm name=”Eddy”/>
<page id=”two”/>
<page id=”three”/>
<page id=”four”/>
<worm name=”John”/>
<worm name=”bill”/>
</library>
???? How do I delete all “worm” nodes?
If I throw in another delete of delete doc.worm; after doing the initial delete, it works, but there should be a way to say “all nodes” where ever they are (which is what I thought delete doc..*.worm was doing????
Rick,
Even though you can use delete with a hardcoded E4X syntax like “delete doc.worm”, using “delete” with an E4X expression always returns a runtime errors.
I personally feel it’s better to let the “delete” operator work on individual nodes.
“delete” will always work on an XML child. Note the use of this WHILE loop and accessing the XML child with brackets, instead of XMLList.
var search:String=”worm”;
while(doc..*.(name() == search).length() > 0)
{
delete doc..*.(name() == search)[0];
}
Ah thanks Keith, very interesting. I’ll try it that way.