11. Message Type Helpers

The AMPS JavaScript client includes a set of helper classes to make working with AMPS easier. The provided static TypeHelper class is used for fine grained control over message types. It is also used for creating and parsing composite message types.

By default, the client supports the following message types:

  • JSON;
  • FIX / NVFIX - via FixTypeHelper class;
  • Binary;
  • Composite types built from the combinations of the above types.

Messages of these types are automatically parsed into native JavaScript data structures and are ready for consumption.

Composite Message Types

The TypeHelper.compositeHelper static method handles creating and parsing composite message types:

// Register the json-xml-json-binary composite message type
amps.TypeHelper.helper(
    // The name of the new composite message type
    'compositejxjb',

    // create the composite type helper
    amps.TypeHelper.compositeHelper(
        'json',    // Part 1: JSON
        'xml',     // Part 2: XML
        'json',    // Part 3: JSON
        'binary'   // Part 4: Binary
    )
);

Example 11.1: Register a composite type helper

The composite type helper created with TypeHelper.compositeHelper will automatically build data to send from the array of the message parts and parse multi-part messages from the received raw data. All types used in the new composite type helper should be registered before creating it.

Building Composite Messages

Once the composite type helper was registered, it becomes very easy to build composite messages of that type. In the following example, we register a new composite message type, compositejjjb that consists of three JSON parts and one binary part. Then, we create the composite message and publish it. The type helper will take care of converting message parts into the raw message data that will be sent:

// Before creating a client, register the composite message type
amps.TypeHelper.helper(
    'compositejjjb',
    amps.TypeHelper.compositeHelper(
        'json', 'json', 'json', 'binary'
    )
);

var client = new amps.Client('composite-sender');

client
    .connect('ws://localhost:9000/amps/compositejjjb')
    .then(function() {
        // create the array with message parts
        var parts = [
            {id: 5, value: 'part1'},         // JSON part 1
            {value: 'part2', data: 22.22},   // JSON part 2
            {value: 'part3', data: 33.33},   // JSON part 3
            'XXXXXXXXXXXXXXXXXXXXXXXXXXX'    // Binary data
        ];

        // Publish the composite message
        client.publish('composite-topic', parts);
    });

Example 11.2: Publish a composite message

Parsing Composite Messages

Once the composite type helper is registered, the composite messages of that type will be parsed automatically. The Message.data field will contain the array of message parts, parsed according to their types:

client.subscribe(function(message) {
    var parts = message.data;
    console.log('Received message with', parts.length, 'parts');

    for (var i = 0; i < parts.length; ++i) {
        console.log('Part ' + i + ': ', parts[i]);
    }
}, 'composite-topic');

Example 11.3: Consume a composite message

Notice that the receiving application is written with explicit knowledge of the structure and content of the composite message type.

Custom Type Helpers

If a message type used in your application is not supported, it is possible to create new type helpers. Another situation in which you might need a custom type helper is when the default implementation does not fit your needs. For example, the default implementation of FIX / NVFIX message types assumes that the keys in each message are unique, thus overriding values if the same key occurs twice in the same message. If your messages contain duplicated keys and that is expected behavior, you need to override the default type helper with a custom implementation.

Each type helper is an object that must contain the following methods:

  • serialize(data: any): string[] - this method is used to serialize data in order send it to the server. All data chunks should be converted into an array of strings.
  • deserialize(data: any): any - this method deserializes data from the Server into a format that will be consumed by the message handler. It can be any format as long as the consumer of the data is aware of it.

Below we provide some examples on how to utilize TypeHelper functionality.

Create New Type Helpers

Create and register a custom type helper for XML messages:

var xmlTypeHelper = {
    serialize: function(data) {
        return [new XMLSerializer().serializeToString(data)];
    },
    deserialize: function(data) {
        if (data.constructor === String) {
            return new DOMParser().parseFromString(data);
        }
        else {
            // Binary buffer, need to decode utf8
            return new DOMParser().parseFromString(
                decodeURIComponent(escape(Uint8ToString(data)))
            );
        }
    }
};

// Register the above XML custom helper for parsing XML messages
amps.TypeHelper.helper('xml', xmlTypeHelper);

Example 11.4: Custom Type Helper for XML Messages

Override Default Type Helpers

In case the custom parsing behavior is expected, it is possible to override the default type helper:

/**
* create a JSON type helper that does not parse JSON data into native
* JS objects keeping it in form of a string
*/
var jsonHelper = {
    serialize: function(data) {
        return [data];
    },
    deserialize: function(data) {
        return JSON.stringify(data);
    }
};

// override the default type helper
amps.TypeHelper.helper('json', jsonHelper);

Example 11.5: Overriding the default type helper

Register the custom type helper for NVFIX:

var nvfixTypeHelper = {
    serialize: function(data) {
        // already formatted fix/nvfix string
        if (typeof data === 'string') {
            return [data];
        }

        // otherwise, we assume it's an object with keys and values
        return [
            Object.keys(data).map(function(key) {
                return key + '=' + data[key];
            }).join('\x01') + '\x01'
        ];
    },
    deserialize: function(data) {
        var parsedData = {};
        String.fromCharCode.apply(null, new Int8Array(data))
            .split('\x01')
            .slice(0, -1)
            .map(function(keyValue) {
                 var keyValueTuple = keyValue.split('=');
                 var key = keyValueTuple[0];

                 // no '=' inside of the value
                 if (keyValueTuple.length === 2) {
                     parsedData[key] = keyValueTuple[1];
                 }
                 else {
                     parsedData[key] = keyValue.slice(key.length + 1);
                 }
            });

        return parsedData;
    }
};

// Register the above NVFIX custom helper for parsing NVFIX
amps.TypeHelper.helper('nvfix', nvfixTypeHelper);

FIX / NVFIX delimiters

In some cases the delimiter value used in FIX / NVFIX messages differs from the default (\x01). In this situation you don’t have to implement a custom type helper; instead, the custom delimiter can be set:

amps.TypeHelper.helper('nvfix').delimiter('%01');

Example 11.6: Custom Delimiter for NVFIX