Since GI 3.0 debuted in 2005 with the Common Data Format (CDF), GI has enforced a standard XML data schema with a root element called data, nested elements called record and record attributes such as jsxid, jsxtext and jsximg. This schema applies to the classes that implement the jsx3.xml.CDF interface: Select, Tree, Menu, Table and Matrix (and previously List and Grid).
<data jsxid="jsxroot"> <record jsxid="1" jsxtext="One" jsximg="one.gif"/> <record jsxid="2" jsxtext="Two" jsximg="two.gif"/> ... </data>
There are a lot of benefits to a standard data schema like this such as simplicity and interoperability. However, a significant problem is that few services expose data as CDF unless they happened to be designed specifically for GI. Ever since 3.0 we've been coming up with new ways to work around this problem.
All of the CDF controls use XSLT to transform their XML data sources into HTML for rendering. GI 3.0 included the XSLURL, XSLId and XSLString properties (in jsx3.xml.Cacheable), which allowed the default XSLT template to be overridden for a particular instance of one of the CDF classes. So a developer could copy jsxtree.xsl to their project, change, for example, @jsxtext to @label and set an instance of Tree to use the modified template.
This approach had a number of problems, the most significant of which was that the template files were implicitly part of the API of each class and would need to be forward compatible with new releases. If we wanted to add support for a new CDF attribute like @jsxstyle (and then if Tree.js somehow depended on the new template behavior) all previously branched templates would break. When I realized this in the mid 3.x's we decided that supporting custom templates was clearly untenable and I deprecated the functionality.
At the same time I introduced a replacement called XSL transformers. This feature remains supported today though it has its own issues. Using XSL transformers a developer can define one or more XSL transformations to convert the source data to CDF before it goes into the XML application cache and is used to render a CDF control. This has the advantage of being completely declarative (compared with a custom JavaScript schema conversion). It can also modify the structure of the source XML so that the CDF ends up with more or fewer records than the source XML. However, it forces the developer to know XSL, which is not necessarily a common skill for front-end developers.
In 3.9 I've come up with a solution that I think is better than both of the previous approaches. It accomplishes 90% of what XML transformers does and is much simpler. It also enables scenarios that neither of the previous approaches did like:
- The core CDF schema elements and attributes data, record and jsxid can be renamed to anything
- A single data source can drive multiple CDF controls, each with its own view of the data
- No additional files need to be loaded over HTTP
Here's how it works. Every CDF control has a schema property of type jsx3.xml.CDFSchema. The value of this property defines how the control views its CDF datasource. In other words it defines its schema. If the schema property is not set then the control uses the default CDF schema, which is the same as the schema used in 3.0-3.8.
To implement this feature in 3.9 each CDF control, instead of querying its datasource for, let's say, the jsxtext attribute of a record, first queries its schema for the name of the text attribute and then queries its datasource for that attribute of a record. For example, in 3.8 jsx3.gui.Tree included many expressions like
this.getRecordNode(id).getAttribute("jsxtext")
In 3.9 this type of expression has been rewritten as
this.getRecordNode(id).getAttribute(this.getSchema().getProp("text"))
This additional level of indirection allows the CDFSchema object to control how each CDF control views its datasource. The default CDFSchema object returns "jsxtext" from a call to getProp("text"). However, you can modify the schema so that it returns "label" instead. This change would allow you to have a CDF datasource like
<data jsxid="jsxroot"> <record jsxid="1" label="One"/> ... </data>
In 3.9 you can control this new functionality from within Builder. To do so,
- Open or create a component with a CDF control in it, such as Matrix, Tree or Menu.
- Find the CDF Schema component in the Component Libraries palette and drag it onto the CDF control. It may be easier to drag it onto the corresponding node in the Component Hierarchy palette.
- Modify the properties of the schema object in the Properties Editor.
In the Properties Editor you'll find an editable property for each attribute in the default CDF schema. In general, the default value of property abc is jsxabc. CDFSchema also has a property called "record." This property allows you to change the element name of the records in the datasource. You can choose another name, such as "item" or you can use set it to "*" to indicate than any XML element should be interpreted as a data record.

For the CDF schema shown in the screenshot, I will have a datasource that looks like
<items id="jsxroot"> <item id="1" label="One"/> <thing id="2" label="Two"/> <object id="3" label="Three"/> ... </items>
Using the same datasource for two different controls is simply a matter of defining two different CDF schemas for the controls. I've put together a simple example project: GI-702.zip.
Enjoy, and leave any feedback here as a comment on this post or on the JIRA ticket.

Comments (2)
Sep 30, 2009
Tony Lefebvre says:
Can a shema be shared between multiple GUI component ?Can a shema be shared between multiple GUI component ?
Sep 30, 2009
Jesse Costello-Good says:
I haven't implemented that mainly because I haven't settled on a nice way of ser...I haven't implemented that mainly because I haven't settled on a nice way of serializing schemas other than as the direct children of the object they apply to. However, you can programmatically call setSchema() on two different components and pass the same schema object.