Sending and receiving data with submission

The XForms submission module provides many different ways to read data into a form, and to send processed data back out again.

An introduction to XForms submission

This introduction contains a high-level look at XForms submission. For those new to a declarative approach to programming it's probably easier to understand what XForms submission does by looking at the kinds of procedural code it replaces. Since the approach most familiar to web programmers will be the use of JavaScript in an Ajax application we'll begin there, before then moving on to XForms.

Ajax submission

Ajax emphasises asynchronous programs, by way of JavaScript. Programmers used to languages such as Java or C++ will probably find the idea quaint. But as the explosion of interest in Ajax in the last few years has shown, programmers using HTML forms as their application's front-end were crying out for a way to remove unnecessary round-trips to the server and page refreshes.

There are a number of ways this can be achieved in Ajax programming, but the most common is to avoid using HTML forms, and instead use a separate object which can retrieve data directly. This data can then be used to update small parts of the display, and of course dramatically improves the user experience.

The script

Recall that the only reason we're looking at the way data is transferred using script, is as a way to understand what it is that XForms is making easier. If you're looking at XForms with an Ajax background, then this will be useful information, but if you've not used script or Ajax before, don't feel the need to learn any of the following constructs--just try to get a high-level view.

The most common way of doing an Ajax request is to set up script like this:

var req;

function loadXMLDoc(url) {
  // native XMLHttpRequest object
  if (window.XMLHttpRequest)
  {
    req = new XMLHttpRequest();
    req.onreadystatechange = readyStateChange;
  // IE/Windows ActiveX version
  } else if (window.ActiveXObject)
    req = new ActiveXObject("Microsoft.XMLHTTP");

  if (req);
  {
    req.onreadystatechange = readyStateChange;
    req.open("GET", url, true);
    req.send();
  }
}

function readyStateChange() {
  // '4' means document "loaded"
  if (req.readyState == 4)
  {
    // HTTP 200 means "OK"
    if (req.status == 200)
    {
      // do something here
    } else
    {
      // error processing here
    }
  }
}

loadXMLDoc("http://example.com/customers/my-customer.xml");

A more complete approach would look to handle the error conditions, allow more than one submission to be running at the same time, and so on. But since that would make the code more complicated, not less, the snippet we have gives us plenty to help us understand what it is that a data submission is generally doing.

The XForms equivalent

Let's map the procedural code that we've just seen to the XForms equivalent:

<xf:submission method="get" action="http://example.com/customers/my-customer.xml">
  <xf:action ev:event="xforms-submit-done">
      <!-- do something here -->
  </xf:action>
  <xf:action ev:event="xforms-submit-error">
      <!-- error processing here -->
  </xf:action>
</xf:submission>

Here we can see that we've specified the same things that we did in the Ajax approach--the URL that we want to get data from, the actions to perform if there is an error, and so on--but the difference here is that this is all we've had to do. Everything else involved in the submission has been done for us.

Submission as a pattern

This idea of simply 'filling in the blanks' will be very familiar to anyone who takes a 'pattern' approach to their development. By re-using commonly occurring programming 'patterns', programmers are able to develop applications more quickly and more accurately. A pattern is not necessarily a library of code though, but is more a set of relationships between high-level, abstract components.

Let's think of our XForms submission processing as a pattern. What we have is some behaviour that is defined in a pretty abstract way, along the following lines:

'get' some data from one URL, or 'put' some data to another URL, and when done, generate either a 'success' event or a 'fail' event.

The strength of any pattern is in the fine balance between being sufficiently general that it can apply to many situations, but not so general that it says nothing. In this case it does seem quite vague; all we seem to have said is 'get a document from here and put it there, and let me know when you've finished'. But when you think about it, that pretty much covers most of the basics for a lot of things we'd like to do: sending an email, saving a file to disk, retrieving an RSS feed, posting a document to a server, and so on. It certainly captures the basic Ajax request we looked at earlier.

By operating at such a high-level of abstraction, we provide a lot of power to the programmer whilst also reducing the amount that they need to learn to perform new tasks.

Reading data from files

To illustrate, let's look at the XForms mark-up for retrieving a file from disk:

<xf:submission
 method="get" action="file:my-customer.xml"
/>

As you can see, unlike with other programming paradigms, no new API is needed to make the shift from HTTP to the file system. In other words, any author/programmer who mastered the most common technique--getting and putting data to or from an HTTP server--has already got the complete skill-set for getting and putting the same document to or from the hard-drive.

Note also that if the code was written as a relative path:

<xf:submission
 method="get" action="my-customer.xml"
/>

then the same XForm would work regardless of whether it was running on a server, or being loaded from a local disk.

Sending an email

Let's go further and see what new skills our author needs to acquire to send the same XML document by email--and whether we can save them the cost of buying a book on MAPI, and the time needed to read it:

<xf:submission
 method="put" action="mailto:john.doe@example.com?subject=Results"
/>

As before, no new API to learn. No need to roll your sleeves up and get to grips with MAPI so that you can write a powerful application that will allow a user to enter some XML data, validate it, and then send it via email.

The declarative approach

This 'hiding' of functionality is the key point. It's not that we've hidden a whole load of script behind a set of libraries or common functions--which of course it goes without saying that we have--but rather that in XForms we have a simple language that captures an enormous amount of generic functionality. But crucially, that generic language still allows us to use the functionality in very specific situations.

To put it the other way round, we still get specific functionality, such as saving files, sending emails, storing data in a database, and so on, but without having to learn lots of specific APIs. (And we haven't even begun to look at the other features this simple pattern captures, from validation through to different data serialisation options, which make this an even more powerful technique than we've seen so far.)

In the following sections will look at how to use the power of submission in more detail.

Submission processing

The submission element is not an action handler, or a form control, but a collection of attributes that guide: how to obtain some data to be sent, whether to validate it first, how to transport the data, what to do with any data that is returned, and so on.

Submission should therefore be seen as a series of phases, with the various attributes controlling what happens at each step of the process:

  1. dispatching of the xforms-submit event to indicate that sending data is about to begin;
  2. selection of the fragment of data to be sent;
  3. validation of the data;
  4. serialisation of the data into a format that can be transferred;
  5. initiation of the data transfer;
  6. if running asynchronously, then the submission returns at this point, and other processing continues until the transfer is complete. If running synchronously, then processing is paused until the transfer is complete;
  7. when the transfer is complete, the returned data is checked for errors;
  8. if there are no errors then the data is placed in the target location, and xforms-submit-done is dispatched;
  9. if there are errors, nothing happens to the data, and xforms-submit-error is dispatched.

Note that if new data arrives and is placed in a target, a rebuild of the model that holds the instance will be necessary.

We'll look at all of these steps in more detail in the following sections.

Selecting the data for submission

The data to be submitted is selected by evaluating the expression in the ref attribute. If no attribute is present, then its value is "/", which in effect means the first instance in the containing model. If a value for @ref is present, it represents the root of a fragment which will be used to find the data to be submitted. This obviously means that it is not necessary to submit an entire instance--smaller parts can be sent.

Sometimes an author will not want to serialise any data for submission, and for this reason XForms 1.1 adds the serialize attribute which can be set to false to indicate no data should be sent. But assuming this attribute is set to true, or is not set at all, then starting at the root of the fragment, all relevant nodes--i.e., all nodes that have their relevant Model Item Property evaluating to true--are collected to make up the data to be sent.

A common pattern is for authors to use relevance only to control the way that the user interface appears to their users (since non-relevant controls are hidden), but to still want all of the data in the instance to be submitted. To make this possible, XForms 1.1 introduced the relevant attribute, which can be set to false to indicate that even non-relevant nodes should be serialised for submission.

Note that the value in @ref also sets the evaluation context for any XPath expressions that might occur within the submission element.

The Submission method

The submission 'method' indicates the operation to be performed. Although this will often be a simple mapping to an HTTP method, it needn't be. For example, the following code sets up a submission declaration that when called will push data up to a server, using an HTTP PUT:

<xf:submission action="http://www.example.org/customer/3.xml" method="put" />

However, the following submission declaration differs only in that the file: protocol is used, yet it has a completely different effect--instead of sending data over the network using the HTTP protocol, the data is simply saved to a local file:

<xf:submission action="file:///my-files/customers/customer/3.xml" method="put" />

In other words the submission method is more than just an HTTP method.

(For more on the file: protocol, see Manipulating local files.)

Abstract methods

The best way to understand the submission method therefore is as a request to the XForms processor to do something, but without having to be protocol-specific about what you want doing. This ability to specify what you want to do in a high-level way is very powerful. For example, say we would like to create a form that uses both of the URLs we had above--the customer on the server, and one on our local drive. The easiest way to achieve this would be for our form to use relative paths, and then if we run the form from our disk it saves the information to the local file, and if we run the form from the server it sends the data to the server. In terms of specifying the location it's easy; we just use relative paths as we might do for an image or stylesheet in HTML. But the method would normally be more of a problem, since the action to 'save a file' is completely different to the action to 'send data to a server using HTTP'. However, since the submission method is merely a request to the XForms processor to do something in a protocol-independent way, then the following mark-up will work in both of our scenarios--server and local drive alike:

<xf:submission action="customers/customer/3.xml" method="put" />

In short, the exact meaning of 'put' will be determined by the context in which it is being used, and it might even mean 'send an email':

<xf:submission action="mailto:accounts@example.org" method="put" />

Setting the submission method

We saw above that the submission method is usually specified using the method attribute; this is true in both XForms 1.0 and XForms 1.1, However, in XForms 1.1 the submission method can also be set using the method child element, which will override the method attribute if both are specified.

(Note that there is no default value for the submission method so either an attribute or element will be needed.)

The element technique allows us to set the submission method at run-time which is a particularly useful way of reusing one submission declaration for a number of different purposes, as we often want to do when building RESTful applications.

For example, in our earlier examples we had some customer information stored at:

customers/customer/3.xml

In a RESTful application we might allow this data to be updated with a 'put' or removed completely with a 'delete'. With XForms 1.0 we'd have to have two separate submission declarations:

<xf:submission id="sub-put" action="customers/customer/3.xml" method="put" />

<xf:submission id="sub-delete" action="customers/customer/3.xml" method="delete" />

However, in XForms 1.1 we can reuse one submission declaration for both tasks, as follows:

<xf:submission id="sub-customer" action="customers/customer/3.xml">
  <xf:method value="method" />
</xf:submission>

Now that this submission declaration has been created, it can be used in any number of ways. For example, the submission method need not be set until the user chooses an action:

<xf:trigger>
  <xf:label>Save</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:setvalue ref="method">put</xf:setvalue>
    <xf:send submission="sub-customer" />
  </xf:action>
</xf:trigger>

<xf:trigger>
  <xf:label>Delete</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:setvalue ref="method">delete</xf:setvalue>
    <xf:send submission="sub-customer" />
  </xf:action>
</xf:trigger>

Alternatively, if we want to keep things more encapsulated, we might put this functionality into the submission declaration itself:

<xf:submission id="sub-customer" action="customers/customer/3.xml">
  <xf:method value="method" />
  <xf:action ev:event="my-save">
    <xf:setvalue ref="method">put</xf:setvalue>
    <xf:send submission="sub-customer" />
  </xf:action>
  <xf:action ev:event="my-delete">
    <xf:setvalue ref="method">delete</xf:setvalue>
    <xf:send submission="sub-customer" />
  </xf:action>
</xf:submission>

This provides us with a convenient way of centralising any interaction with the customer into one place, which is particularly useful for keeping code up-to-date, or for managing processes that need to run when another has completed (or failed). To use this submission declaration the triggers we saw earlier would need to change to:

<xf:trigger>
  <xf:label>Save</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:dispatch name="my-save" target="sub-customer" />
  </xf:action>
</xf:trigger>

<xf:trigger>
  <xf:label>Delete</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:dispatch name="my-delete" target="sub-customer" />
  </xf:action>
</xf:trigger>

This makes the purpose of the code easier to see and read, and consequently easier to maintain.

Validation of the selected data

After the selection step, the data to be submitted is then validated. No validation events are dispatched during this step, but the whole process of validation is the same as defined in xforms-revalidate. If any of the selected data has its required property set, but is found to be empty, or is invalid according to its constraint properties or a schema, then submission processing is stopped after dispatching event xforms-submit-error.

Since there will be situations where invalid data needs to be submitted--for example to save an incomplete form--XForms 1.1 introduced the validate attribute, which can be set to false to prevent this step of the processing. Validation is also disabled if @serialize is set to false, but since there may be situations where in invalid instance should halt submission, even if no data is being serialised, this can be overridden by setting @validate explicitly.

Specifying the URL and headers at run-time

Although submission is a powerful feature of version 1.0 of XForms, there are a number of common use-cases that it is unable to support. For example, although it's easy for an XForms 1.0 form to request an RSS feed and to display the data with a repeat, it's not possible for the user to specify the feed location at run-time, perhaps by typing a URL into an input control, or selecting from a list. XForms 1.1 adds a number of new features to submission to control the URL, request headers, location of returned data, and more, which together make it much easier to build forms that use servers based on REST, ATOM, SOAP, and WebDAV.

Use-cases

In addition to the RSS feed example, other configurations that require URIs to be defined at run-time include REST-based services. For example, Basecamp incorporates the user's name into the URL, and as such it is impossible to create a generic form with XForms 1.0, even though XForms is ideal for communicating with such services.

Perhaps the most important example of a service that can't be used with XForms 1.0 is ATOM. Whilst at a push a form could be created to provide support for Basecamp (by creating one form per person!) there is simply no workaround that would allow ATOM to be used, since the URL that should be used for adding and editing items is provided within the item itself (a process called introspection).

Just as important as setting the URL at run-time is the need to specify request headers; both WebDAV and SOAP, for example, require header values to be set, as does HTTP authentication. (Whilst most XForms processors support HTTP authentication using the usual pop-up login box, by being able to set headers on a request it's possible for a form author to create their own login mechanism.)

An example, using SOAP

A good example of the need for headers is when making a SOAP request. The action to be performed on the server is usually passed as a header in the HTTP request. Using the XForms 1.1 additions, this could be configured as follows:

<xf:submission
 ref="instance('inst-request')"
 method="post"
 replace="none"
>
  <xf:resource value="instance('inst-control')/submission/@action" />
  <xf:header>
    <xf:name>SOAPAction</xf:name>
    <xf:value value="concat(
      '&quot;http://schemas.xmlsoap.org/wsdl/http/',
      local-name(instance('inst-request')/soap:Body/*),
      '&quot;'
     )"
    />
  </xf:header>
</xf:submission>

One nice feature of this example is that the 'method' to use in the SOAPAction header is calculated for us automatically from the actual SOAP payload.

Allowing the user to choose a URI

Whatever technique is used to get the URI into the submission, a little trick you can use to allow the user to choose a file is to use the xf:upload control to obtain a URL, as follows.

First, create a submission that uses a dynamic URL:

<xf:submission
 ref="instance('inst-request')"
 method="post"
 replace="none"
>
  <xf:resource value="instance('inst-control')/submission/@action" />
</xf:submission>

Next give the node that will hold the URL a datatype of 'xsd:anyURI', so that it can be used with the xf:upload control:

<xf:bind nodeset="instance('inst-control')/submission/@action" type="xsd:anyURI" />

Finally, use an xf:upload control to allow the user to provide a URL:

<xf:upload ref="instance('i-config')/submission/@action">
  <xf:label>Choose file</xf:label>
</xf:upload>

The xf:upload control is often used to 'upload' the contents of a file into the instance data, but by binding the control to a node that has a datatype of xsd:anyURI we tell the control that all we want is the URL for the file, and not the file itself.

submission with replace="text", and the target attribute

New features are due to be added to the XForms specification, to allow submission to handle incoming non-XML data. A new possible value for the replace attribute--text--prevents a submit-error occurring on receipt of non-XML data, whilst a new attribute--target--contains an XPath expression indicating the node into which response data is to inserted.

Here is an example of how to use the new attributes to insert the content of the file SomeTextFile.txt into the node /x/y in the instance data.

<xf:model>
  <xf:instance id="i0">
    <x>
      <y />
    </x>
  </xf:instance>
  <xf:submission
   method="get" action="SomeTextFile.txt"
   replace="text" target="y"
  />
</xf:model>

Although this has not been fully discussed and decided by the PTB, here is my interpretation of the changes to be made to Section 11: Submit of the XForms spec:

5.

  • For a success response including a body of a any media type (i.e. with a content type not matching any of the specifiers in [RFC 3023]), when the value of the replace attribute on element submission is "text",

    • If there is no target attribute, nothing in the document is replaced and submit processing concludes after dispatching xforms-submit-error.
    • If a target attribute is specifed, A node from the instance data is selected based on the value of the target attribute, the context for the execution of this XPath expression will be the same as the context for the resolution of the ref attribute. The value of this node will be replaced by the returned text, in accordance with section 10.1.9 setvalue.
  • For a success response including a body of an XML media type (as defined by the content type specifiers in [RFC 3023]), when the value of the replace attribute on element submission is "instance", the response is parsed as XML. An xforms-link-exception (4.5.2 The xforms-link-exception Event) occurs if the parse fails. If the parse succeeds, then

    • If there is no target attribute all of the internal instance data of the instance indicated by the instance attribute setting is replaced with the result.
    • If a target attribute is specifed, A node from the instance data is selected based on the value of the target attribute, the context for the execution of this XPath expression will be the same as the context for the resolution of the ref attribute. The parsed xml data is then inserted as a fragment into the selected node.

    Once the XML instance data has been replaced, the rebuild, recalculate, revalidate and refresh operations are performed on the model, without dispatching events to invoke those four operations. Submit processing then concludes after dispatching xforms-submit-done.

  • For a success response including a body of a non-XML media type (i.e. with a content type not matching any of the specifiers in [RFC 3023]), when the value of the replace attribute on element submission is "instance", nothing in the document is replaced and submit processing concludes after dispatching xforms-submit-error.

More interesting uses can be seen when submitting to a server that supports XPointer queries. Consider the situation where a single value is required from a large XML document; prior to the introduction of this feature, the following unwieldy code would be required:

<xf:model>
  <xf:instance id="iRS">
    <dummy />
  </xf:instance>
  <xf:instance id="i">
    <x>
      <y />
    </x>
  </xf:instance>
  <xf:submission
   method="get" action="AnEnormousXMLFile.xml"
   instance="iRS" replace="instance"
  >
    <setvalue ev:event="xforms-submit-done"
     ref="instance('i')/y"
     value="instance('iRS')/some/deeply/nested/node"
    />
  </submission>
</model>

With the target attribute, and @replace="text", it can be condensed into the following:

<xf:model>
  <xf:instance>
    <x>
      <y />
    </x>
  </xf:instance>
  <xf:submission
   method="get" action="AnEnormousXMLFile.xml#xpointer(/some/deeply/nested/node/text())"
   target="y" replace="text"
  />
</xf:model>

A spin-off from this feature is that the target attribute can also be used in conjunction with @replace="instance", and so build up complex documents by inserting retrieved sub-trees into the current instance, instead of replacing it entirely. This can be particularly handy when dealing with large XForms documents containing instance data with potentially many optional sections. In this way, it can work like relevance, but in reverse, adding the sections you need, rather than disabling those you don't.

For example:

<xf:model>
  <xf:instance>
    <x>
      <y />
    </x>
  </xf:instance>
  <xf:submission
   method="get" action="ASmallXMLFile.xml"
   target="y" replace="instance"
  />
</xf:model>

The above example would insert the XML content of the document at ASmallXMLFile.xml, into the y element.

Manipulating local files

XForms processors are not required to support the file: protocol, but both formsPlayer and the Firefox extension do. The methods applicable to file: will usually be get and put. In formsPlayer, the approach is to prompt the user on any load or save of a local file, since this is obviously a very powerful technique.

In addition to the file: protocol, formsPlayer implements an experimental protocol called cookie:, which allows forms to load and save XML documents locally without the user needing to be prompted. This is deemed safe because the location for the file is only ever going to be in a sub-directory in the user's local store (i.e., home directory). There is therefore no way for a form to get out of this area and load or overwrite system files.

In addition, the URL for the sub-directory is based on the domain of the site being used, so there is also no way for a form from one site to get access to data from other sites (sites can share data across forms, though, which is very useful).

Typical uses are chaining forms together, where the output for one form is the input to the next, or saving state across an application. An example of the latter is illustrated in our Google Desktop app (to be added) which allows you to save searches across sessions.

Initialising a local file

Whether using file: or cookie:, a common requirement is the need to create the initial file on first use of a form. This is a little tricky, because if you initialise your instance using @src you have no way to detect that the file doesn't exist. The following technique is used by our RSS Reader side-bar (part of the formsPlayer install, and discussed at [to be added]):

  1. Create an instance that will hold the contents of the file. Give it an inline instance of some default values--in our case it contains a basic list of RSS feeds--rather than referring to the file using @src. In the rest of this example, the instance is referred to as i-list.
  2. Set up a submission to load the required document. In our case it's going to be a configuration file, loaded via the cookie: protocol, but it could be loaded via the file: protocol:
    <xf:submission
     id="sub-open-list"
     method="get"
     ref="instance('i-list')"
     action="cookie://feed-list.xml"
     replace="instance"
    >
      <!--
        If we fail to load the list, then this is probably
        the first time we've been run, so save the default
        list.
      -->
      <xf:action ev:event="xforms-submit-error">
        <xf:message level="modal">
          RSS Reader has failed to open your local data file.
          This may be because this is the first time you have
          used the RSS Reader. A default data file will be
          created which includes a small number of feeds.
        </xf:message>
        <xf:send submission="sub-save-list" />
      </xf:action>
    </xf:submission>
    

    Note the behaviour in the error handler (when the file doesn't exist)--we simply save our initial instance data, which has been placed inline in the instance i-list).

  3. Create an xforms-ready handler that triggers this get, i.e., which loads your local document:
    <xf:send submission="sub-open-list" ev:event="xforms-ready" />
    

    (Note that you have now effectively emulated the behaviour of @src, but with all the benefits of submission, such as having notification events as well as the ability to use instance data.)

    In normal operation, using sub-open-list will load the previously saved file, overwritting the inline instance data. But on first use there will be no file, and so the error handler will be run, and cause the inline instance data to be saved.

  4. Set up a submission to save the list to the file (you don't need to set a 'dirty flag' but it is quite useful):
    <xf:submission
     id="sub-save-list"
     method="put"
     ref="instance('i-list')"
     action="cookie://feed-list.xml"
     replace="none"
     encoding="ISO-8859-1"
     omit-xml-declaration="false"
     indent="true"
    >
      <xf:setvalue bind="dirty-flag" ev:event="xforms-submit-done">
        false
      </xf:setvalue>
    </xf:submission>
    

    Note that you would need these two submissions anyway, to load and save the file; the only additional bit we're adding here is the use of xforms-ready to invoke the submission that does the loading, and the use of xforms-submit-error on that load to invoke a save of our initial data.

HOWTO: Processing XForms data on a server

This tutorial is for people who are familiar with the basics of XForms, have knowledge of some server-side processing langauge, and want to combine the two. In this section we'll look at how to do this with PHP, ASP, Java Servlets and ColdFusion.

To run the examples in this document you will need to have installed formsPlayer. For the ColdFusion example you should have installed and configured ColdFusion 6.1. The ASP example requires IIS and the Java Servlet example requires Jakarta Tomcat or an equivalent. The PHP example was tested with PHP version 4.3.7 and IIS.

The source for the form and the server-side scripts is available from here.

The sample XForm

The examples that are listed in this document all use the same simple XForm. It contains only one XForms model and one set of instance data. The XForm holds a few pieces of information about a person such as their name, age, gender, address, etc. The full instance data is as shown below:

<person xmlns="">
  <firstname>Joe</firstname>
  <surame>Bloggs</surame>
  <gender>M</gender>
  <age>32</age>
  <phone>02085555555</phone>
  <email>joe.bloggs@example.com</email>
  <address>
    <addr1>1a Street</addr1>
    <addr2>A Town</addr2>
    <addr3>A City</addr3>
    <addr4>Post Code</addr4>
  </address>
</person>

Each piece of instance data is referenced by a single XForms control. The XForms data is submitted to a resource by using the submission element. For the ASP example the submission element will look something like this (depending on the structure of your IIS site):

<xf:submission id="sub-save-person"
  method="post" action="http://localhost/EchoPerson.asp" 
/>

NOTE: Although the same XForm is used in each example in the following sections, you will need to change the submission elements' action attribute for each one so that the form data is submitted to the correct resource.

ASP and XForms

This example shows how to obtain and manipulate submitted XForms instance data within an Active Server Page. Our ASP is called EchoPerson.asp, which takes the submitted XML and outputs it to the browser in a tabular format. (The full ASP code is available here.)

In the ASP, to get hold of the XML instance data, we create an MSXML DOMDocument and load the HTTP Request (the submitted XML) into it, using the load method, as follows:

     ... 
     var oXML = new ActiveXObject("MSXML2.DOMDocument");
     oXML.load(Request);
     ...

Now we have the instance data in the DOMDocument we can retrieve values from it to set our script variables. Using the selectSingleNode XPath function we return a node from which we can get the value:

     ...
     var oFirstName = oXML.selectSingleNode("/person/firstname");
     var sFirstName = oFirstName.text;
     var oSurname   = oXML.selectSingleNode("/person/surname");
     var sSurname   = oSurname.text;
     ...

Now we have the instance data values stored into our JavaScript variables we can do as we want with them, in this case just format them for display using an HTML table:

     ...
     var sHTML = "";
     sHTML += ";"
     sHTML += ";"
     ...
     sHTML += "
First name : " + sFirstName + "
Surname : " + sSurname +
" ...

Once we have built up the complete HTML string we return it to the browser:

     ...
     Response.Write(sHTML);         

To try this for yourself:

1. Extract the person.html and EchoPersonDataASP.asp files from the xfserverscripts.zip (see 'Prerequisites') to the root directory of your default IIS site.
2. Open the person.html file in notepad, or other text editor, and change the submission element action attribute to "EchoPersonDataASP.asp".
3. Open up Internet Explorer and go to http://localhost/person.html
4. Change any data and submit the form.

ColdFusion (6.1) and XForms

As with the previous examples, this one shows how to output the submitted XForms data in an HTML table but this time we are using ColdFusion 6.1 as the scripting language. (The full ColdFusion code is available here.)

After submission of the XForm, the first thing to do in the ColdFusion page is to get hold of the request data and put it into a variable. This is achieved using the GetHttpRequestData() system function:

     ...
     
     ...     

The GetHttpRequestData() function gets ALL of the request data, the headers and the body. We are only interested in the body of the request in this example which is obtained with the content parameter. Once we have that, we convert it to a string using ToString and parse it, using the XMLParse function:

     ...
     
     ...              

Now we have the XML document available to our ColdFusion page we can do what we want with the data. In our sample it is just to display the values in an HTML table:

     ...
     <td>
          First name
     </td>
     <td>
          #xmlDoc.person.firstname.XmlText#
     </td>
     ...

To try this example for yourself:

1. Install and configure ColdFusion 6.1 (Trial version available from Macromedia site).
2. Extract the person.html and showdata.cfm files from the xfserverscripts.zip (see 'Prerequisites') to the same directory in the site you have configured to use with ColdFusion.
3. Open the person.html file in notepad, or other text editor, and change the submission element action attribute to "showdata.cfm".
4. Open up Internet Explorer and go to your website eg. http://localhost/person.html
5. Change any data and submit the form.

Java servlets and XForms

This example shows one way to obtain and manipulate submitted XForms instance data within a Java Servlet. Our Servlet is called EchoPersonDataServlet, which takes the submitted XML and outputs to the browser in a tabular format the names and values of the XML elements. Any empty elments are ignored. (The full PHP code is available here.)

NOTE: If you wish to compile and use the EchoPerson Servlet with Tomcat you will need to have added the following .jar files to your classpath. %TOMCAT_HOME%\common\lib\servlet-api.jar, %TOMCAT_HOME%\common\endorsed\xercesImpl.jar and %TOMCAT_HOME%\common\endorsed\xmlParserAPIs.jar. You will also have to configure the web.xml file in your web application to reference the Servlet.

In the Servlet, to get hold of the XML instance data, we read in the body of the request to a string:

     ...    
     String xml      = new String("");
     String thisLine = new String("");
     ...
     BufferedReader br = request.getReader();
     while ((thisLine = br.readLine()) != null) {
          xml = xml.concat(thisLine);
     }
     ...              

Now we have the instance data in a string we will use DocumentBuilderFactory and DocumentBuilder objects along with the Document interface to help manipulate it. From java.sun.com - "The Document interface represents the entire HTML or XML document. Conceptually, it is the root of the document tree, and provides the primary access to the document's data".

     ...
     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
     try {
          DocumentBuilder builder  = factory.newDocumentBuilder();
          Document        document = builder.parse(new InputSource(new StringReader(xml)));
     ...

All the rest of the Servlet does is to iterate through the nodes of the XML and format the names and values of the elements in an HTML table which is returned to the browser to be displayed.

NOTE: How to manipulate the submitted XForms data and whether to use DOM or SAX to do it is really up to the developer and it depends what the exact needs are. For more information on using XML and Java please take a look at the Java Web Services Tutorial.

To try this example for yourself:
NOTE: Installing and configuring Tomcat goes beyond the scope of this tutorial but listed below are the major steps involved in running this tutorial. For complete instructions on using web applications with Tomcat please follow the documentation available on the Apache website.

1. Install and configure Jakarta Tomcat 5 (download from The Apache Site).
2. Make sure that your CLASSPATH contains references to the servlet-api.jar, xercesImpl.jar and xmlParserAPIs.jar files that are mentioned earlier in this section.
3. Create your own 'webapp' structure and extract the person.html file to the root of it.
4. Extract the EchoPersonDataServlet.java file to the 'classes' directory of your webapp and compile.
5. Use the web.xml file for your webapp to map the EchoPersonDataServlet class to a URL.
6. Open the person.html file in notepad, or other text editor, and change the submission element action attribute to the value of the URL you specified in the web.xml file. Remember Tomcat defaults to use port 8080.
7. Start Tomcat, open Internet Explorer and point it to the location of the person.html file.
8. Change any data and submit the form.

PHP and XForms

NOTE: You can download the PHP installer from http://www.php.net. This example was tested with the default installation. The PHP 4.3.7 installation also includes expat, an XML toolkit written by James Clark that parses XML in C. This is an event based XML parser which we will make use of.

This example shows how to obtain and manipulate submitted XForms instance data within a PHP script. Our PHP script is called EchoPerson.php, and it simply outputs the received data to the browser in a tabular format. (The full PHP code is available here.)

The first thing we need to do in the script is to make sure that there is some data for us to process. Any submitted data will be available to us in the HTTP_RAW_POST_DATA global variable. We check to see if this has a value and if it has, assign the data to a local variable:

     ... 
     if (!empty($GLOBALS['HTTP_RAW_POST_DATA'])) {
    
          $req = $HTTP_RAW_POST_DATA;
     ...
     }
     ...

Once we have some data, we create an XML parser, set up the event handler functions--using xml_set_element_handler() and xml_set_character_data_handler(), and then set any other options we need with xml_parser_set_option:

     ...
     $xmlParser = xml_parser_create();
     xml_set_element_handler($xmlParser, "startElement", "endElement");
     xml_set_character_data_handler($xmlParser, "characterData");
     xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, FALSE);
     ...

The startElement() and endElement() functions are defined further up in the script and these functions are where we put any code to call when the parser comes across a new element or finishes reading an element. The characterData() function is also defined further up the script, and this deals with what to do with data contained inside the element. Within these functions we also reference two global variables $currentEle and $sHTML. The first is to keep track of the name of the current element that is being processed, and the other will hold the HTML that will eventually be displayed:

     ...
     $currentEle = "";
     $sHTML      = "";
     
     function startElement($parser, $name, $attrs) {
          global $currentEle;
          global $sHTML;
          $currentEle = $name;
          if (!ignoreElement($currentEle)) {
               $sHTML .= "$name";
          }     
     }
     
     function endElement($parser, $name) {
          global $currentEle;
          $currentEle = "";
     }
     
     function characterData($parser, $data) { 
          global $currentEle;
          global $sHTML;
          if (!ignoreElement($currentEle)) {
               $sHTML .= "$data";
          }
     }
     ...

There is also one more function defined in the script (ignoreElement()) that is used by a couple of the parser event handlers. As we have elements that don't contain data (person and address) we don't want to display them in our HTML table; the elements we don't want to display have their names contained in an array so that we can compare the current element name (global variable $currentEle) with the values in our array, and decide to ignore the element if we have a match:

     ...
      function ignoreElement($eleName) {
          $ignore      = false;
          $ignoreThese = array(0 => "person", 
                               1 => "address",
                               2 => ""
                              );
          foreach ($ignoreThese as $value) {
               if ($value == $eleName) {
                    $ignore = true;  
                    break;
               } 
          }
          return $ignore;
     }    
     ...    

Now we actually parse the XML using the conveniently named xml_parse() function. This returns either true or false depending on whether there were any errors or not. If there are any errors we just add them to the HTML string that has been built up:

     ...
     if(!xml_parse($xmlParser, $req, TRUE)) {
          $sHTML .= "";
          $sHTML .= "An error occurred : ";
          $sHTML .= xml_error_string(xml_get_error_code($xmlParser));
          $sHTML .= "Line : " . xml_get_current_line_number($xmlParser);
          $sHTML .= "Column : " . xml_get_current_column_number($xmlParser);
          $sHTML .= "";
     }     
     ...

After finishing off the HTML table and freeing up the parser memory (using xml_parser_free()) we output the HTML:

    ...
     print $sHTML;
    ...

NOTE: Documentation for all PHP functions, including those for the XML parser are available online at http://www.php.net/docs.php.

To try this for yourself:

  1. Get the person.html and EchoPerson.php files, and place them in the root directory of your website.
  2. Open person.html in notepad, or some other text editor, and change the submission element action attribute to EchoPerson.php.
  3. Open up Internet Explorer and go to http://localhost/person.html, or wherever you have located the file.
  4. Change any data and submit the form.
  5. The browser page should be replaced with the output form the server.