Flex 3, XML Schemas & automatic mapping of AS classes to XSD element definitions (Part 2)

This is a set of posts documenting my experiences with utilising functionality within the Flex SDK that provides the automatic mapping of custom ActionScript classes to element definitions within an XML Schema (XSD).

1.  Part 1 – Introduction and background
2.  Part 2 – Overview of Flex’s XML Schema classes (inc example code)

Part 2 – Overview of Flex’s XML Schema classes (including example code)

This post covers how to take advantage of these classes (including example code) – as at the time of writing there doesn’t seem to be any discussion of these features by Adobe or the community at large. I’ll also list a few caveats we have come across in using these classes.

First things first. The set of classes I will be referring to are found in the mx.rpc.xml package. If you look at the official documentation you will only find a handful of classes that support simple XML encoding and decoding. This is because the majority of the classes relating to XML Schema contain [ExcludeClass] metadata that excludes them from documentation and code hinting.

The easiest way to get visibility of these classes in your code is to place a copy of this package in your project and comment out all the [ExcludeClass] tags above the class definitions.

The key classes are:

  • SchemaManager (manages multiple schema definitions)
  • Schema (manages a single XML Schema Definition)
  • SchemaLoader (manages the loading of an XML schema (including any imports/includes) at runtime)
  • SchemaTypeRegistry (maps an XML schema type to an ActionScript Class)
  • XMLDecoder (decodes an XML document into ActionScript objects based on a XML Schema definition
  • XMLEncoder (encodes an ActionScript object into XML based of an XML Schema)

Example and explanation:

A really basic example Flex app that covers the core functionality of these classes can be viewed here (right click for source).

The primary steps are outlined below:

1. Create an instance of SchemaLoader and asynchronously load an XML schema from a given URL

This automatically loads any other schemas that are defined in XSD import or include elements.

schemaLoader = new SchemaLoader();
schemaLoader.addEventListener(SchemaLoadEvent.LOAD, schemaLoader_loadHandler);
schemaLoader.load("example.xsd");

2. Once the schema is loaded, add it to the SchemaManager and register any ActionScript classes to their corresponding schema type:

private function schemaXMLLoader_loadHandler(event:SchemaLoadEvent):void
{
    schema = event.schema;
    schemaManager.addSchema(schema);
    schemaTypeRegistry.registerClass(new QName(schema.targetNamespace.uri, "example"), ExampleVO);
}

3. Load an XML file based off that schema.

4. Once the XML is loaded, decode the contents using XMLDecoder. Any classes registered in the schemaTypeRegistry will be used when decoding the xml

var xmlDecoder:XMLDecoder;
var qName:QName;
var result:*;

xmlDecoder = new XMLDecoder();
xmlDecoder.schemaManager = schemaManager;

qName = new QName(schema.targetNamespace.uri, "results");
result = xmlDecoder.decode(xml, qName)

5. Encode a custom ActionScript class back into XML using XMLEncoder. XMLEncoder.encode() supports various ways to define the corresponding element in the schema  (top level element, a specific type or even a custom XSD definition) that will be used to encode the Actionscript object.

var qName:QName;
var xmlEncoder:XMLEncoder;
var xmlList:XMLList;

qName = new QName(schema.targetNamespace.uri, "Example");
xmlEncoder = new XMLEncoder();
xmlEncoder.schemaManager = schemaManager;
xmlList = xmlEncoder.encode(exampleVO, qName);

For futher explaination check out the source inside the example applicatio.

Issues and Notes

There are a few things to be aware of when using these classes.

1. It requires a XML Schema to already be in memory when decoding a loaded XML file. As this is an asynchronous task it is best to load the schemas required before loading any XML files based off them. The alternative is to check each XML file for schema information when they load, and delay the decoding of the data until that schema has then loaded. This would require additional checks and steps each time xml data is loaded.

2. Any additional schemas included or imported within the top level schema loaded by SchemaLoader must be referenced using an absolute path. This is because SchemaLoader assumes that relative paths are relative to the main application swf URL (and not relative to the original XSD url).  As these two locations are unlikely to be the same in a real world project, changing these URLs to be relative to the swf will make it work with Flex, but will prevent any other XSD editor from being able to find the corresponding includes or imports.

As this will be an issue when relying on any XML API using relative paths that is outside the direct control of the Flex developer, I would say this is a bug and have submitted it to Adobe as such (link). Feel free to add your support to it.

—————————–

UPDATE July 2010

I’ve  created an updated version of this example project that demonstrates some of the more advanced scenarios that people have posted questions about. This version targets flex 4.0 – but the code base is essentially identical to the old 3.x versions. I’d always recommend targeting the most recent 3.x or 4.x version of the flex rpc library as the flex team seem to make improvements in every release. Having said that, these mx.rpc libraries aren’t something i’m using on a regular basis – so i’m not actively checking what’s new or changed in each release.

This example includes the following additional scenarios/use cases:

  • encoding to multiple ActionScript classes
  • encoding/decoding from multiple actionscript classes
  • encoding/decoding attributes as well as elements
  • nested child elements decoding to an ArrayCollection
  • basic validation of an XML file against the XSD

view it here (right click to view source)

22 Responses to “Flex 3, XML Schemas & automatic mapping of AS classes to XSD element definitions (Part 2)”

  1. misprintt blog » Blog Archive » Flex 3, XML Schemas & automatic mapping of AS classes to XSD element definitions (Part 1) Says:

    [...] 1.  Part 1 – Introduction and background 2.  Part 2 – Overview of Flex’s XML Schema classes (inc example code) [...]

  2. Will Says:

    Cool – glad to see some blogging on it from the Flex side of the equation. :)

  3. Roy Garnaat Says:

    I just started out with Flex and XSD. It is really hard to find good online information about this topic. This is a killer tutorial, just what i needed for now. Thanks!

  4. Anthony Says:

    Hi.
    What if you want to return an ArrayColelction of ExampleVO objects.

    I’m trying to extend your sample here, but I’m having some trouble.

    Thanks for your work!

  5. gonte Says:

    Hi everyone,
    i’m developing XSD-To-AS3 Class Generator, a simple Air app that allow developers to quickly build any Flex/Flash app XML based.

    It takes an XML schema and an XML document and uses it to generate a class library. These classes map directly to data contained in your XML Schema, the generated classes is strongly typed, it forms a kind of template for the developer, ensuring that the data created conforms the underlying XML Schema.

    It will be very usefull for quickly work with 3th party API RESTful interface!

    The project is still in progress
    Try it Adobe Air Marketplace

    ..:: The Ria Team ::..

  6. William Power Says:

    I have a strange problem and I’m hoping you can help me.

    I cannot get FlexBuilder to access the following classes in mx.rpc.xml:
    Schema, SchemaManager, SchemaLoader, XMLEncoder, XMLDecoder

    and the following classes in mx.rpc.events:
    XMLLoadEvent, SchemaLoadEvent

    When I say “access”, I mean that when I type out “mx.rpc.xml.” I don’t see those classes. Also, when I type “new Schema()” there’s no auto-sense for it.

    If I “take it on faith” that the classes are there and just use them, the classes exist and are visible in the debugger. I only discovered that, of course, after messing with FlexBuilder settings for hours (in-between trying to find web sites mentioning this), and also uninstalling and reinstalling FlexBuilder itself. Yuck.

    Any thoughts?

    Thanks,
    Will

  7. William Power Says:

    I’m an idiot. Don’t answer that. The “Exclude Class” stuff didn’t jump out at me when I scanned the post. X|

  8. William Power Says:

    So your original example.xml file has the following root tag:

    and when you re-encode you lose the schemaLocation, noNamesspaceSchemaLocation, and the xmlns:xsi schema instance tag:

    That sort-of renders any output xml unusable, right? Have you found a way around that?

  9. William Power Says:

    (darn them for not having ‘preview’ on the comments section)
    So your original example.xml file has the following root tag:
    <result
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
    xmlns=”http://www.misprintt.net”
    xsi:schemaLocation=”http://www.misprintt.net schema.xsd”
    xsi:noNamespaceSchemaLocation=”http://www.misprintt.net example.xsd” >

    and when you re-encode you lose the schemaLocation, noNamesspaceSchemaLocation, and the xmlns:xsi schema instance tag:
    <result xmlns=”http://www.misprintt.net”>

    That sort-of renders any output xml unusable, right? Have you found a way around that?

  10. dom Says:

    Hi William,

    I haven’t found a proper solution so far. In the past i have just manually re-added them to the xml after the default XMLEncode class has encoded the objects. I’m not sure if it is an issue with the flex XML schema classes, or something that needs to more explicitly defined in the xsd.

    I’m not surprised that there is an issue. Because XML is a native type in AS3, dealing with namespaces in XML is notoriously awkward. In other projects I have resorted to complex regular expressions to strip out all namespace references from an xml string rather than have to juggle multiple namespaces!

  11. darrendb Says:

    Is there a way to just validate the xml against the schema? I don’t want/need the auto-created VO’s, i just want to examine the xml and throw errors if anything is invalid… does that happen implicitly during decodeXML()? Thanks!

  12. henry000 Says:

    Hi, this is great, thanks.

    I do have one question: I have a well-structured complex xsd with several level of nested child-elements. My corresponding class would look like this:

    class MainValueObject
    {
    something1: array; //array of another class “Something1″
    something2: array; //array of another class “Something2″
    … etc …
    }

    “Something1″ and “Something2″ also contain arrays of other classes.

    So would this automated mapping of schema to AS object work?

    How can I register the multiple levels of nested classes against the one xsd schema?

    Thanks.

  13. Diego Says:

    Hi,

    I’m having the same issues as henry000, Do you have any ideia about It?

    thanks

  14. YosiDov Says:

    My question is darrendb question, I need to validate my XML, and receive an error if something is wrong, that’s all…

    Some ideas?

    Thanks…

  15. kuchino Says:

    Hi, how would one use the encode() method if i have for example an mxl looking like this:

    joe

    natty

    jaddy

    naomi

    joel

    nike

    now in your XMLDecode() function,
    the result.example as ExampleVO; was stored in a variable exampleVo;

    and in your XMLEncode() function,
    the value of that variable was passed to an object type o . pointing to the primary node of your xml ->example
    o.example = exampleVO; and then it is attached to the Node
    qname(o, qname);

    that method selects the node passsed in and displays everything beneath. in my own case, itz not working. suppose you have the given xml above. where you have a root object, and an immediate element which has nested elements, how do you use the XMLEncode funntion or the QName() method to get the whole XML. mine is only displaying the root node and not the elements..

  16. kuchino Says:

    in your example, in the encodeToXML() function,
    where is the example in the statement:
    o.example = exampleVO;
    from? i am having a difficult time figuring out which of the files is this? is it the .xml, xsd or just the name of your element? let us know pls.

  17. kuchino Says:

    I have been able to use your example to work on a very complex xml file but the problem is while it decodes properly, it encodes in a way such that the elements are not in the right position. i mean, the artributes are considered as elements. i am guessing itz the statement i am intested in knowing.. when i debug the exampleVO instance of the ExampleVO class , i have the right Objects but when i pass it to o.example, it bhaves funnily.. please elaborate on that statement/line. thanks

  18. dom Says:

    Hi Kuchino,

    ‘exampleVO’ is my example data object created in the ‘decodeXML’ method.
    ‘o.example’ is a temporary object containing a reference to exampleVO.

    The reason that this reference is added to ‘o.example’ rather than just ‘o’ is because source/destination example xml has a parent element called ‘result’ that contains the ‘example’ element.

    I’m essentially reflecting that structure in the object that i send to the XMLEncoder.

  19. dom Says:

    Hi Kuchino,
    I’m not exactly sure what the issue is here – in my experience XMLEncoder faithfully serialises both elements and attributes correctly. The critical thing is to ensure that the object you send to encode has the same structure as the ‘result’ object created in the decodeXML function.

  20. dom Says:

    Hi henry,
    I’ve added a new example to the bottom of the post that includes an xml file with an array of child elements as well as multiple ActionScript value objects. I hope this helps

  21. Abc Says:

    Hi,
    Thanks for the great example and the explanation, it really helped us!
    The link for the view source for your latest update seems to be broken?

  22. Niko Says:

    Have you by chance found any way to address the performance implications of dealing with large XML documents (e.g. XMLLists with a large number of child elements)? I’ve found that the XMLDecoder is fine for small data sets, but it converts XML at a rate of about 100 objects/second, which given the nature of flash, ends up causing the flash player to hang and eventually causes the classic “script timeout” error. Anything upwards of a couple hundred items becomes harmful to the user experience, and anything in the range of 800-1000 items is effectively unusable.

Leave a Reply