JavaScript Data Binding (inputTypes/outputType annotation)

Table of Contents

In a dynamically typed language like JavaScript, each argument or parameter passed into a function can have any type - it can be a string, a number, an object, whatever. But internally the function typically operates on a specific type of data. Maybe the function does a calculation, in which case the data must be in (or be converted to) numerical form.

JavaScript has rich facilities for converting datatypes - for instance if a function or operation expects a string, you can still pass it a number and javaScript will convert the number to a string automatically. But there are limits to the ability of JavaScript to convert freely from one type to another - for instance converting a string to a number requires the programmer to specify how the conversion is done using the parseInt or parseFloat methods.

So despite the dynamic character of JavaScript, function parameters typically have a restricted set of types that they operate on, and the caller of the function needs to be aware of what types are allowed or preferred by the function. In JavaScript the programmer typically relies on examining the source code of the function - or of the comments or other human-readable documentation to determine what the types of parameters are.

When you are invoking a function remotely as a web service operation, examining the source code is less of an option. Having some machine-readable information about the type allows the client to provide some assistance (e.g., ?stub) to the programmer. In addition, the web service may be invoked from a language other than javaScript, so even determining the javaScript type might be somewhat irrelevant. A set of types that can be used across a variety of programming languages and environments is necessary. Just as XML provides a platform- and language-neutral way to format data, the XML Schema Language provides a way to define types. When you expose a web service, the type of the data, including both simple types like strings and numbers, and complex structures, can be described in XML Schema.

To address these issues, the Mashup Server provides the inputTypes annotation to allow the programmer to declare the expected type of input for each parameter of the operation as an XML Schema type or as a javaScript type which is eventually converted to an XML Schema type. Although this isn't strictly necessary for the exposure of the operation (we'll map the parameters into an XML message) having accurate type information makes for a much nicer experience for the user of your function. The outputType annotation serves a similar purpose for the return value.

The inputTypes property is a javaScript object having a number of properties, which is equivalent to the number of parameters of the javaScript function. The names of those properties are the names of the function parameters, and the value of the respective parameter is the intended type (in order to declare XML schema types directly the "xs:" prefix should be used. If the "xs:" prefix is not available its assumed as a JavaScript(plus E4X) type) for the function parameter.

                countWords.inputTypes = {"content" : "string" , "ignoreHyphens" : "boolean"};
                countWords.outputType = "xs:integer";
                function countWords(content, ignoreHyphens) {
                    .....
                }
            

Intepretation and treatment of JavaScript types

Although type information can be provided as javaScript types, WSO2 Mashup Server eventually converts them to XML schema data types. This is done to ensure that the description (WSDL 1.1, WSDL2) which is provided for your service will have rich type information. If the type is specified without an "xs:" prefix they are assumed to be javaScript (plus E4X) types and is mapped into the XML Schema type system as follows:
Declared JavaScript (plus E4X) Type Treatment
String | string Treat as xs:string
Number| number Treat as xs:float
Boolean | boolean Treat as xs:boolean
Date | date Treat as xs:dateTime
Array | array Treat as xs:anyType*
Object | object Generate XML structure
Xml | XML | xml Treat as xs:anyType
Xmllist | XMLList | XMLlist | xmlList | xmllist Treat as xs:anyType*
Any | any xs:anyType
None | none No value.

Each of these tokens has alternative case representations rendering the tokens pseudo-case-insensitive, allowing obvious case ?misspellings?.

In most cases you can be more precise by using XML schema types, for instance using the xs:integer type instead of number when only integer values are expected.

Using XML schema built in types

.inputTypes and .outputType can specify a built-in schema type, indicated by using the ?xs:? prefix.

When used as an outputType, the return type is serialized as defined below, wrapped in a <return> ?parameter?, and further wrapped in a targetNamespace-qualified RPC wrapper. The schema reflects the use of parameter and rpc wrappers, and uses the declared type as the type of the return parameter.

When used as an inputType, the parameter value deserialized by removing the parameter and rpc wrappers, and converted to a javascript native type as defined below.

Declared type Serialization/Desialization
xs:string string
xs:normalizedString string
xs:token string
xs:language string
xs:Name string
xs:NCName string
xs:ID string
xs:IDREF string
xs:NMTOKEN string
xs:ENTITY string
xs:NOTATION string
xs:anyURI string
xs:hexBinary string
xs:base64Binary string
xs:float number
xs:double number
xs:duration number
xs:integer number
xs:nonPositiveInteger number
xs:negativeInteger number
xs:long number
xs:int number
xs:short number
xs:byte number
xs:nonNegativeInteger number
xs:unsignedLong number
xs:unsignedInt number
xs:unsignedShort number
xs:unsignedByte number
xs:positiveInteger number
xs:decimal number
xs:boolean boolean
xs:dateTime date
xs:date date, time component droped
xs:time date, date component droped
xs:gYearMonth date, time and day components droped
xs:gMonthDay date, time and day components droped
xs:gYear date, time, month and day components droped
xs:gDay date, time, year and month components droped
xs:gMonth date, time, year and day components droped
xs:NMTOKENS string
xs:IDREFS string
xs:ENTITIES string
xs:QName string
xs:anyType E4X XML object

Support for optional parameters and arrays

To indicate a parameter is optional, an input type token can be followed by a ?. For example:

                test.inputTypes = {"required" : "string", "optional1" : "number?", "optional2" : "boolean?"};
                function test(required, optional1, optional2) {
                }
            

Messages that omit optional parameters will result in those parameters being assigned the ?undefined? value. Optional parameters will be dispayed as minoccurs="0" in the schema.

Similarly, an input parameter can be an array. This is modeled in the schema as maxOccurs="unbounded", and repeated elements of the same name are collected into an Array, with items in the array following the normal mappings for that type. The postfix '+'; can be added to an input type (except for 'array' itself) to indicate that maxOccurs='unbounded' should be added.

The '*' postfix is equivalent to the presence of both '?' and '+', except that zero matching elements in the message results in an empty Array rather than 'undefined'.

Support for simple String-Valued enumerations

Simple enumerations are a common use case for defining a custom simple type. Additionally they provide a high value to the user to have in a description format (e.g enumerations can be converted into a drop down list in the try-it).

A type that is a list of string values separated by '|' is treated as an enumeration.

                accountInfo.inputTypes = {"type" : "silver | gold | platinum"};
                accountInfo.outputType = "paidup | arrears | unknown";
                function accountInfo(type) {
                    ...
                }
            

Support for Raw values In/Out

Without an outputType annotation, a returned value might be of any of the javascript types and therefore an automatic conversion to XML is done. If the return value is not XML itself the data in the return value will be returned wrapped in an element called return which will also have a js:type arribute specifying the JavaScript type returned.

Dynamic type Wrapper Value serialization
String <return js:type="string">value</return> Value serialized as xs:string
Number <return js:type="number">value</return> Value serialized as xs:float
Boolean <return js:type="boolean">value</return> Value serialized as xs:boolean
Date <return js:type="date">value</return> Value serialized as xs:dateTime
Undefined <return js:type="undefined"></return> No value
Array <return js:type="array">value</return> Value serialized as in Array -> XML
Object <return js:type="object">value</return> Value serialized as in Object -> XML
XML <return js:type="xml">value</return> Serialize directly as XML
XMLList <return js:type="xmllist">value</return> Serialize children directly as XML

Wrapped vs Unwrapped behaviour

The Mashup Server will wrap all your request and responses by default in RPC type wrapper elements. These wrapper elements provide a conventional structure that can be easily interpreted as function parameters by many different platforms. It provides a means to serialize simple data values such as strings in XML. Wrapper elements come in handy when using the stub, because the stub generator recognizes this convention and can provide the user a uniform interface. If you are interested in preserving the structure and types of your parameters and return values, we advise you to use the default behaviour. The wrapper element name of the request will be in the form of <operationName> and the wrapper element name of the response will be of the form <operationNameResponse>. The following example illustrates this.

                function echoString(param) {
                    return param;
                }
            

The wire-level XML input to this function is as follows (with "value" used as the parameter"):

                <p:echoString xmlns:p="http://services.mashup.wso2.org/test?xsd">
                    <param>value</param>
                </p:echoString>
            

The wire-level XML output from this function is as follows:

                <ws:echoStringResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">
                    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="string">value</return>
                </ws:echoStringResponse>
            

Although providing wrapper elements is the default behaviour the user can always overide this. If you want to instruct the mashup server not to use wrapper elements you should set the inputTypes annotation or the outputType annotation (depending on your requirement) to "#raw". This instructs the mashup server to exclude wrapper elements. Note that if you decide to use raw XML then the input to the function will be the XML value itself. The following example illustrates this use.

                echoString.inputTypes="#raw";
                echoString.outputType="#raw";
                function echoString(param) {
                    return param;
                }
            

The wire-level XML input to this function is as follows (with "<value/>" used as the parameter"):

                <value/>
            

The wire-level XML output from this function is as follows:

                <value/>
            

As you can see that "#raw" forces the mashup server to exclude the wrapper elements.

Array -> XML conversion

When arrays are returned from an operation the mashup server serializes the array to XML as follows. Take the following function as an example,

                returnArrayFunction.outputType="array";
                function returnArrayFunction() {
                    var returnArray = new Array();
                    returnArray.property1 = "value1";
                    returnArray.property2 = 2.270;
                    returnArray.property3 = <value>2</value>;
                    return returnArray;
                }
            

Thie output of the function would be serialized as follows:

                <ws:returnArrayFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">
                    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="array">
                        <property3 js:type="xml">
                            <value>2</value>
                        </property3>
                        <property1 js:type="string">value1</property1>
                        <property2 js:type="number">2.27</property2>
                    </return>
                </ws:returnArrayFunctionResponse>
            

As arrays can contain multiple elements they are always wrapped in a return element with the js:type attribute set to indicate that the return was an array.

Alternatively an array could have been declared as follows (Note that the array properties are declared using indices):

                returnArrayFunction.outputType="array";
                function returnArrayFunction(){
                    var returnArray = new Array();
                    returnArray[0] = "value1";
                    returnArray[1] = 2.270;
                    returnArray[2] = <value>2</value>;
                    return returnArray;
                }
            

In this case the response will look as follows. Instead of having the elements as properties the elements will be as items because they have no name of course.

                <ws:returnArrayFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">
                    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="array">
                        <item js:type="string">value1</item>
                        <item js:type="number">2.27</item>
                        <item js:type="xml">
                            <value>2</value>
                        </item>
                    </return>
                </ws:returnArrayFunctionResponse>
            

Object -> XML conversion

Serialization of Objects is identical to that of Arrays with the following exception. The type 'js:object' is used. Take a look at the following example:

                returnObjectFunction.outputType="object";
                function returnObjectFunction() {
                    var returnObject = new objectFunc();
                    returnObject.property1 = "value1";
                    returnObject.property2 = 2.270;
                    returnObject.property3 = <value>2</value>;
                    return returnObject;
                }

                objectFunc.visible=false;
                function objectFunc(){
                }
            

This is the response:

                <ws:returnObjectFunctionResponse xmlns:ws="http://services.mashup.wso2.org/test?xsd">
                    <return xmlns:js="http://www.wso2.org/ns/jstype" js:type="object">
                        <property3 js:type="xml">
                            <value>2</value>
                        </property3>
                        <property1 js:type="string">value1</property1>
                        <property2 js:type="number">2.27</property2>
                    </return>
                </ws:returnObjectFunctionResponse>