AsBeanGen – Generate value objects for DTD driven XML files
Update:
Download AsBeanGen tool
Download AsBeanGen source
I fixed some of the bugs mentioned in the comments:
- stripping '-' and '_' chars from the bean names
- handeling recursive DTD
- added 'implicit getters' mode
- new option to generate interfaces
.....................................................................................................................................................
AsBeanGen is a class generator application written in java which generates as3 data bean classes from DTD files. Overall it shortens development time a lot, because it rids you of spending a considerable amount of time on writing data bean classes.
Instead AsBeanGen can generate hunderts of classes in less than a second for you and as an added bonus it doesn't just generate the bean classes, but also the classes which transform the content of an XML file (implementing the DTD) to data bean objects. So finally you just have to write two lines of code yourself to get a strongly typed object representation of an XML file.
If you never heard of DTD (Document Type Definition), I suggest you to read this.
As DTD doesn't support type information natively, you also have to annotate the DTD files through simple comments to add the neccessary type information, otherwise AsBeanGen uses 'Object' as the standard type.
To give you a more specific idea how it is used, let's jump straight into an example:
This is the annotatd DTD file
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT test (size,fonts+) > <!-- Sets the types for the properties of the SizeData class TYPE size.width=int TYPE size.height=int --> <!ELEMENT size EMPTY > <!ATTLIST size width CDATA #REQUIRED height CDATA #REQUIRED > <!-- Sets the types for the properties of the FontsData class TYPE fonts.path=String Generates 'getFontDataById(id:String):FontData' method into FontsData class GENERATE font.id Generates 'getFontDataByName(name:String):FontData' method into FontsData class GENERATE font.name --> <!ELEMENT fonts (font+) > <!ATTLIST fonts path CDATA #REQUIRED > <!-- Sets the types for the properties of the FontData class TYPE font.id=String TYPE font.type=String TYPE font.name=String TYPE font.src=String TYPE font.systemfont=Boolean --> <!ELEMENT font EMPTY > <!ATTLIST font id CDATA #REQUIRED type CDATA #REQUIRED name CDATA #REQUIRED src CDATA #REQUIRED systemfont (true|false) #IMPLIED >
This is an xml file based on the DTD
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test SYSTEM "test.dtd"> <test> <size height="100" width="100"/> <fonts path="fonts/"> <font id="foo" name="Arial" src="arial.swf" type="text"/> <font id="foo2" name="Arial2" src="arial2.swf" type="text2"/> </fonts> </test>
This is the generated FontsData bean class
package data.test { /** * Class generated by AsBeanGen * http://www.splink.org */ import data.test.fonts.FontData; public class FontsData { private var _path : String; private var _fontData : Array; public function FontsData() { } public function getPath() : String { return _path; } public function setPath(value:String) : void { _path = value; } public function getFontDataById(id:String) : FontData { var data:FontData; var r:FontData = null; for (var i:int=0; i<_fontData.length; i++) { data = _fontData[i]; if(data.getId() == id) { r = data; break; } } return r; } public function getFontDataByName(name:String) : FontData { var data:FontData; var r:FontData = null; for (var i:int=0; i<_fontData.length; i++) { data = _fontData[i]; if(data.getName() == name) { r = data; break; } } return r; } /** * @return an array of FontData objects */ public function getFontDataArray() : Array { return _fontData; } /** * @param an array of FontData objects */ public function setFontDataArray(value:Array) : void { _fontData = value; } } }
This is the generated FontsDataReader class for the FontsData bean class
package data.test { /** * Class generated by AsBeanGen * http://www.splink.org */ import data.test.FontsData; import data.test.fonts.FontDataReader; public class FontsDataReader { private var _xml : XML; public function FontsDataReader(xml:XML) { _xml = xml; } public function read() : FontsData { var fontsData:FontsData = new FontsData(); fontsData.setPath(_xml.@path); fontsData.setFontDataArray(readFontData()); return fontsData; } private function readFontData() : Array { var rAr:Array = []; for each(var xml:XML in _xml['font']) { var reader:FontDataReader = new FontDataReader(xml); rAr.push(reader.read()); } return rAr; } } }
This is what the generated structure looks like:
This is how to use the generated classes in your application:
// TestDataReader is the root reader and expects the loaded xml file // as a parameter. var reader:TestDataReader = new TestDataReader(xml); // TestDataReader's read method returns the root object of the // generated tree, filled with values from the given xml. var data:TestData = reader.read();
AsBeanGen can be used as a commandline application (usage shown below), or, if you are eclipse user you can add it as an 'External Tool' which proves to be very convenient (at least to me).
java -jar AsBeanGen.jar test.DTD C:\your\project\src de.your.package.name.test
Note that the "test" in the package name is the name of the DTD file.
How to add AsBeanGen as an 'External Tool' in Eclipse
1) Open your annotated DTD file
2) Open the 'External Tools'

3) Create a 'New Launch Configuration'
4) Fill the form (see screenshot)

4) After configuring AsBeanGen, you will want it to appear in the External Tools favorites. Switch to the 'Common' tab and check 'External Tools' within the 'Display in favorites menu' box.
5) Press 'Apply' button

Now you should be able to launch AsBeanGen from the 'External Tools Favorites Bar'. Just make sure that the DTD from which you wish to generate the actionscript classes is opened and focused.

Nice one, Will give it a try
Thanks
Comment by Chetan Sachdev
October 3, 2008 8:39 pm
This looks good, I hate writing these by hand :)
Comment by Mondain
October 5, 2008 7:59 am
So instead you’ll spend your time writing DTDs?
Why aren’t you using AS3 properties for your getters and setters?
Comment by loves to hate
October 5, 2008 5:50 pm
loves to hate:
Well, firstly writing the dtd is a lot less to write than writing all the bean and reader classes. Also changig the dtd and generating the classes again is way faster than to having to adjust both, reader and bean classes.
Furthermore a dtd carrys certain benefits in itself. A dtd is in fact a definition of the structure of a xml file. (maybe a bit like a class which is the blueprint of an object)
Having such a definition enables your xml editor for instance to provide you with decent autocompletion for dtd defined xml files.
Also the descriptive character of dtds can be of great value. If for instance your flash app retrieves xml data from a backend and you have dtds for all the different xml files the backend can serve in place, then you and the backend guys have the dtd as the interface to program to.
Comment by admin
October 5, 2008 6:33 pm
I keep getting this error with a complex dtd:
Exception in thread “main” java.lang.StackOverflowError
at java.util.regex.Pattern$CharProperty.(Pattern.java:3332)
at java.util.regex.Pattern$CharProperty.(Pattern.java:3332)
at java.util.regex.Pattern$BmpCharProperty.(Pattern.java:3363)
at java.util.regex.Pattern$BmpCharProperty.(Pattern.java:3363)
at java.util.regex.Pattern$Single.(Pattern.java:3391)
at java.util.regex.Pattern.newSingle(Pattern.java:2951)
at java.util.regex.Pattern.atom(Pattern.java:1985)
at java.util.regex.Pattern.sequence(Pattern.java:1834)
at java.util.regex.Pattern.expr(Pattern.java:1752)
at java.util.regex.Pattern.compile(Pattern.java:1460)
at java.util.regex.Pattern.(Pattern.java:1133)
at java.util.regex.Pattern.compile(Pattern.java:823)
at java.lang.String.split(String.java:2293)
at java.lang.String.split(String.java:2335)
at org.splink.gen.templates.ClassTemplateHelper.firstCharToLowerCaseArray(ClassTemplateHelper.java:25)
at org.splink.gen.templates.as3.AS3Class.addToImports(AS3Class.java:86)
at org.splink.gen.core.SubelementParser.addValues(SubelementParser.java:106)
at org.splink.gen.core.SubelementParser.parseSubelements(SubelementParser.java:50)
at org.splink.gen.core.Parser.parseSubelements(Parser.java:118)
at org.splink.gen.core.Parser.parseElement(Parser.java:137)
at org.splink.gen.core.SubelementParser.parseSubelements(SubelementParser.java:51)
at org.splink.gen.core.Parser.parseSubelements(Parser.java:118)
at org.splink.gen.core.Parser.parseElement(Parser.java:137)
at org.splink.gen.core.SubelementParser.parseSubelements(SubelementParser.java:51)
Comment by Jonathan Doklovic
December 5, 2008 10:54 pm
hi jonathan,
could you please provide the dtd, so i can look into the problem and hopefully fix it quickly.
max
Comment by admin
December 6, 2008 1:44 am
actually, I found the problem.
I don’t know if it’s the DTD parser code or your code, but there’s a recursion problem that hsould at least be tested for.
The specific line in the DTD that it chokes on is:
I think this is valid in the dtd, but I had to change it to:
To get the as generator to work.
Also, you should probably upgrade to the latest DTD code since it seems they’ve made a lot of changes.
Comment by Jonathan Doklovic
December 8, 2008 5:52 pm
sorry, the code is:
Broken:
<!ELEMENT conditions (condition)*>
Working:
<!ELEMENT conditions (condition)*>
Comment by Jonathan Doklovic
December 8, 2008 5:53 pm
grrrrrr.
Broken code is:
>!ELEMENT conditions (conditions | condition)*<
Comment by Jonathan Doklovic
December 8, 2008 5:53 pm
another issue….
if a DTD element has a – in it’s name, the beangen doesn’t strip it out and causes lots of headaches in the as3 code.
Comment by Jonathan Doklovic
December 8, 2008 7:01 pm
another question….
once I have the Data objects and manipulate them, is there anyway to generate a new XML object from the as3 structure?
Comment by Jonathan Doklovic
December 8, 2008 7:30 pm
generating dtd from the as beans would be great and it actually has been on my todo list for quite some time, but as it’s a complex task and i’m quite busy at the moment, i guess it will take some time until i can start to implement it.
i already knew about the recursion issue but hadn’t any time yet to fix it. maybe i’ll find some time around christmas ;) i will also add the stripping of ‘-’ chars to my todos. hopefully i can release another version early next year – maybe even open source it (after i cleaned up the code a bit)
Comment by admin
December 8, 2008 11:37 pm
Hi, I was wondering if you’d made any new updates to this, or if you’re open to collaboration / opensourcing the code now?
Good work so far.
Thanks.
Jason
Comment by Jason
November 26, 2009 1:53 am
Yes, i have updated asbeangen. here is the donwload link. it now outputs implicit getters if you invoke it with “implicit-getters” as last argument. Furthermore you can define interfaces
( INTERFACE MyInterface:class1,class2 )
you can check out deepsplink whose config is asbeangen generated. It uses the interface feature.
I also fixed some of the stuff Jonathan mentioned (recursion, node names with _ or – chars)
Comment by admin
November 26, 2009 11:21 am
Thanks, any plans to go opensource with AsBeanGen?
Comment by Jason
December 1, 2009 5:20 am
to explain why I’m asking for OpenSource (aside from the obvious)
I’m working on a solution where I need to hook a changing XML feed to a persistent AS3 datamodel, when I update the feed I correlate the model against the in-coming feed, so I can update Flex views over databinding.
Comment by Jason
December 1, 2009 5:28 am
> invoke it with “implicit-getters” as last argument.
java -jar org.splink.AsBeanGen.jar mydtd.dtd package implicit-getters
Throws an “arguments are missing” error.
Comment by Jason
December 2, 2009 2:42 am
jason, you forgot the source folder.
“java -jar org.splink.AsBeanGen.jar mydtd.dtd source-folder package implicit-getters”
about asbeangen open source: i need to polish the sources at least a bit before publishing it. Because i’m currently pretty busy with a deepsplink update this might take some time…
Comment by admin
December 3, 2009 1:26 pm