0% found this document useful (0 votes)
60 views92 pages

Ultra

The Ultra Reference Guide provides comprehensive instructions on the Ultra Format Management utilities within the MediationZone Platform, detailing the Ultra Format Definition Language (UFDL) and its application in creating format definitions. It covers various aspects of format management, including configuration, editing, and decoding of Usage Detail Records (UDRs), as well as the functionalities of the Ultra Format Editor and UDR File Editor. The document emphasizes the importance of defining internal and external format definitions for effective data handling and conversion.

Uploaded by

358169168
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views92 pages

Ultra

The Ultra Reference Guide provides comprehensive instructions on the Ultra Format Management utilities within the MediationZone Platform, detailing the Ultra Format Definition Language (UFDL) and its application in creating format definitions. It covers various aspects of format management, including configuration, editing, and decoding of Usage Detail Records (UDRs), as well as the functionalities of the Ultra Format Editor and UDR File Editor. The document emphasizes the importance of defining internal and external format definitions for effective data handling and conversion.

Uploaded by

358169168
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 92

Ultra Reference Guide

8.1

Copyright © 2019 Digital Route AB


Copyright © 2019 Digital Route AB

The contents of this document are subject to revision without further notice due to continued progress in methodology, design, and
manufacturing.

Digital Route AB shall have no liability for any errors or damage of any kind resulting from the use of this document.

DigitalRoute® and MediationZone® are registered trademarks of Digital Route AB. All other trade names and marks mentioned herein are
the property of their respective holders.
Table of Contents
1. Format Management Overview 5
2. Ultra Format Configuration 7
3. UDR File Editor 12
3.1 Editing a UDR 13
3.2 Editing a Bulk of UDRs 15
4. UDR Views 19
5. Ultra Format Converter 21
6. Introduction to the Ultra Format Definition Language 23
6.1 Building Blocks 23
6.2 The Ultra Module 29
7. External - Sequential Format 32
7.1 Record Declaration 32
7.2 Field Declarations 34
7.3 Expressions 43
8. External - Ericsson IOG/IN Records 44
9. External - ASN.1 Formats 45
10. Internal Formats 49
11. In-maps 52
11.1 Examples of Non-Automatic Maps 53
11.2 Automatic Maps 54
11.3 discard_output Specification 58
12. Decoders 59
13. Out-maps 61
14. Encoders 63
15. A Constructed Decoder Example 64
16. A Sequential Format Example 68
17. An ASN.1 Format Example 72
18. XML Schema Support 78
18.1 Overview 78
18.2 External - XML Records 79
18.2.1 XML Data Type Mapping 79
18.2.2 AnyType UDR Type 80
18.2.3 XML Schema Extensions 80
18.3 IPDR Compliance 80
18.4 XML Schema Limitations 81
18.5 An XML Format Example 81
19. Google Protocol Buffer Support 84
20. Avro Support 89
Ultra Reference Guide
This document describes the Ultra Format Management utilities of the MediationZone Platform.

This user's guide includes a detailed description of the Ultra Format Definition Language and how to create format definitions in the
language syntax.

4
1. Format Management Overview
MediationZone is capable of handling various Usage Detail Record (UDR) formats provided that their structure is known. Vendor UDR
formats are often complex and hard to work with in agents that will view or manipulate data. Therefore, MediationZone converts the
external UDR formats to an internal representation, and vice versa when UDRs are leaving the system.

In order to make this conversion, both internal and external format definitions have to be defined. When the definitions exist, there must be
a mapping description that maps each external field of interest to an internal field.

Ultra does not require a one-to-one relationship between external and internal field names. Normally, only fields of interest or those that are
needed when leaving the system are declared in the internal format definition. Often the internal definition holds additional fields that are
initially empty and then populated on the way through a workflow. MediationZone does not constrain how these fields are used.

Example - Format management

In this example the external field E1C is not needed and not mapped at all. Internally IFA and IFB are manipulated. IFE is given
a value, probably depending on values defined in IFC and IFD during the UDRs way through the workflow. At exit, the two
original fields are mapped into E2A and E2B plus the new (IFE) into E2C.

MediationZone introduces a language called Ultra Format Definition Language (UFDL) that is used when describing UDR formats and
mappings.

Apart from describing external formats, internal formats and mappings, the UFDL requires separate decoder and encoder specifications. A
decoder or encoder refers to one or several mappings that are used for the actual translation.

Format Storage
In order to operate on UDRs, there must be a compiled version (Java class) of the internal format definition available in the Code or Ultra
server. Such Java classes are introduced into the servers in two different ways:

1. Format definitions entered in the Ultra Format Editor are compiled and inserted in the Ultra server when saved.

2. Precompiled format definitions (for instance; Radius, NetFlow, and SNMP protocols) are inserted in the Code server during
package commit.

A format can be recompiled while workflows using it are running. In this case, the changes to the format will take effect the next time the
workflow is activated.

All Java classes generated by the Ultra server are kept in the system. This is to make UDRs, for instance stored in ECS, using the old
format version reprocessable. For further information, see 5. Ultra Format Converter.

5
Note!

Saving a format in the Ultra Format Editor may take some time, since the system may have to re-validate and regenerate many
dependent configurations.

If an Ultra format is moved to another directory, or is renamed, the configurations using the format will become invalid.

MZ Tagged Formats
There are two built-in external formats that can be used between workflows, typically by Inter Workflow agents; MZ Tagged Format and
MZ Tagged (compressed). Using the compressed format will reduce the size of the UDRs significantly. However, since compression
will require more CPU, you should consider the trade off between I/O and CPU when choosing encoder. No Decoder or Encoder needs to
be defined in Ultra to handle these formats.

The MZ Tagged formats contain header data that is based on the time zone settings of the host on which it was generated. This is
important to consider when you compare output from identical workflows that are running on hosts with different time zone settings. The
binary data may differ due to the header content.

Decoding of an UDR
The Ultra engine is designed to decode only the necessary parts of an Usage Detail Record, UDR, to keep workflow performance as high
as possible. By default (that is, provided that Full Decode is not selected), the decoder will only decode enough to calculate the size of the
UDRs. The UDR content will be decoded as the field values are accessed. Hence, if there is a decoding error, this may not be discovered
by the decoder however by the agent accessing a specific field.

Note!

Full decoding of complicated nested UDR structures can be time consuming. In most cases the performance cost of this option
is however small.

6
2. Ultra Format Configuration
The Ultra Format Editor allows users to introduce format definitions in the system to be used by agents in workflows. Definitions are
described using the Ultra Format Definition Language (UFDL).

A saved format definition will have its name displayed in the title. Other than selecting Decoder, Encoder, and UDR Type definitions,
formats can be imported from APL code, as well as directly from other Ultra definitions:

import ultra.folder_name.module_name; // From APL code

import folder_name.module_name; // From UFDL code

To open the editor, click the New Configuration button in the upper left part of the MediationZone Desktop dialog, and then select Ultra
Format from the menu.

Ultra Format Editor

Syntax Highlighting
In the code area, the different parts of the code are color coded according to type, for easier identification, and when right-clicking in the
code area, a context sensitive popup menu appears, enabling easy access to the most common actions you might want to perform.

The text is color coded according to the following definitions:

Blue - Functions

Green - Types

Orange - Comments

Purple - Key words

Yellow/Green - Values

Ultra Format Editor Menu


The main menu changes depending on which configuration type that has been opened in the currently active tab. There is a set of
standard menu items that are visible for all configurations and these are described in the Desktop User's Guide.

The menu items that are specific for Ultra Format Editor are described in the following sections:

7
The File Menu
Item Description

Import... Select this option to import code from an external file. Note that the file has to reside on the host where the client is running.

Export... Select this option to export your code to an *.ufl file that can be edited in other code editors, or be used by other
MediationZone systems.

The Edit Menu


Item Description

Validate Compiles the current Ultra code, checking for grammatical and syntactical errors. The status of the compilation is displayed in
a dialog. Upon failure, the erroneous line is highlighted and a message, including the line number, is displayed.

Undo Select this option to undo your last action.

Redo Select this option to redo the last action you "undid" with the Undo option.

Cut Cuts selections to the clipboard buffer

Copy Copies selections to the clipboard buffer

Paste Pastes the clipboard contents

Find... Displays a dialog where chosen text may be searched for and, optionally, replaced

Find Repeats the search for the last string entered in the Find dialog
Again

8
The View Menu
Item Description

History Each time a configuration is saved, a new version is created. Many versions of a configuration may exist but only the last
version can be modified and executed. The old versions are kept for log and rollback reasons. Select History to examine
old configurations in the Historical Configurations panel. The panel will appear at the bottom of the Configuration tab and
holds a list of all versions. Arrow buttons are used to step back and forward between the different versions. Rollback to an
old version of a configuration is handled by opening and saving the old version. A comment is automatically added, stating
that the current version was created from a historic one.

A configuration history

References Click to see the Reference Viewer listing references to and from the active configuration. The Reference Viewer includes
the following tabs:

Used By: Displays a list of other configurations that refer to the configuration. For example: a workflow group that
refers to a workflow.
Uses: Displays a list of other configurations that the configuration refers to. For example: a workflow configuration
that refers to a specific profile.
Access: Displays the group of users that may access the configuration, and the user that created (owns) the
configuration.

Ultra Format Editor Buttons


The toolbar changes depending on which configuration type that is currently open in the active tab. There is a set of standard buttons that
are visible for all configurations and these buttons are described in the Desktop User's Guide.

The additional buttons that are specific for Ultra Format Editor tabs are described in the following sections:

9
Buttons Description

Validate Compiles the current Ultra code, checking for grammatical and syntactical errors. The status of the compilation is displayed
in a dialog. Upon failure, the erroneous line is highlighted and a message, including the line number, is displayed.

Undo Select this option to undo your last action.

Redo Select this option to redo the last action you "undid" with the Undo option.

Cut Cuts selections to the clipboard buffer

Copy Copies selections to the clipboard buffer

Paste Pastes the clipboard contents

Find... Displays a dialog where chosen text may be searched for and, optionally, replaced

Find Again Repeats the search for the last string entered in the Find dialog

Zoom Out Zoom out the code area by modifying the font size. The default value is 12(pt). Clicking the button between the Zoom Out
and Zoom In buttons will reset the zoom level to the default value. Changing the view scale does not affect the
configuration.

Zoom In Zoom in the code area by modifying the font size. The default value is 12(pt). Clicking the button between the Zoom Out
and Zoom In buttons will reset the zoom level to the default value. Changing the view scale does not affect the
configuration.

Configuration Diff Panel


If you want to compare the current version of an Ultra configuration that you are displaying with a previous version of the same
configuration, you can use Configuration Diff to compare the two versions side by side.

To hide or display the Configuration Diff panel, click the vertical Diff button to the right of the Ultra Format Editor. The panel can be
hidden or visible. By default, it is hidden.

10
Ultra Format Editor - Configuration Diff panel

You can navigate through the differences by scrolling both panes in parallel using the scroll bar to the right of either pane. If you want to
scroll through each pane separately, hover over the pane that you want to scroll through, and use the scroll wheel on your mouse.

If you want to skip through each difference in the panes, you can use the navigation buttons, Previous, Next and Refresh, which are
described below.

Item Description

History Click the History drop box to select the version to which you want to compare the version currently displayed.

You can also display and view encrypted configurations. If you select an encrypted configuration, you are prompted to enter
the relevant password to decrypt the configuration.

Previous Click this button to skip to the previous difference in the configurations. The previous difference is highlighted in green.

Next Click this button to skip to the next difference in the configurations. The next difference is highlighted in green.

Refresh Click this button to display the updated version of the configuration. Configurations are not locked when you display them in
Configuration Diff. If another user has modified and saved a configuration that you are viewing in this tool, you can click this
button to view the latest updated version.

11
3. UDR File Editor
The UDR File Editor allows the user to view and edit the content of UDR files.

The UDR file may be of any format as long as it has an associated decoder. The Open dialog enables decoder selection. Correspondingly,
the Save dialog includes a selection of encoders to use.

Since UDRs often contain a large number of fields, it is possible to create a UDR View to select fields and be viewed directly as columns in
the UDR list (select UDR View... in the Edit menu). Each UDR can also be opened in a separate dialog where all fields are available.

Note!

The UDR File Editor will by default only support input files up to size 3MB. If the file size exceeds this limit, the UDR File Editor
will only read up to 3MB of the file and then stop, showing only the read records.

To support decoding of files larger than 3MB, the Desktop property mz.gui.udreditor.limit must be set for the
corresponding Desktop. See 2.6.9 Desktop Properties in the System Administration User's Guide. Setting the property to yes
means the 3MB limit applies. Setting the property to no will bypass the limitation mechanism, which may potentially cause out of
memory errors in the Desktop.

Several UDR file editors can be used simultaneously, however avoid editing the same UDR from several different editors, since
there is no functionality to handle real-time co-authoring.

To open the UDR File Editor, click the Tools button in the upper left part of the MediationZone Desktop window, and then select UDR File
Editor from the menu.

UDR File Editor

Setting Description

UDR A list from which a UDR view may be selected. A UDR view is a user defined filter which displays additional columns
View containing UDR field data. If(None) is selected, the list will consist of two columns only: Pos and Type. However, if a file is
opened, a third column Orig Pos will become visible as well. For further information, see 4. UDR Views.

Orig Pos Shows the original position of the UDR. New indicates that the UDR did not exist prior to the last Save.

Pos The current position of the UDR. The order applies upon saving.

Type The UDR types

Field All columns following the Type column, will list the field names of the UDR type according to the UDR view selection. If there
Name is no field with the defined name, No such field is stated.

12
Ultra File Editor Menu
The main menu changes depending on which Configuration type that has been opened in the currently active tab. There is a set of
standard menu items that are visible for all configurations and these are described in 2.1 Menus and Buttons in the Desktop User's Guide.

The menu items that are specific for UDR File Editor are described in the following sections:

File Menu
Item Description

Open... Displays a dialog where the local file system can be browsed for files to open. The Decoder used for reading the file is also
configured here.

Full Decode is enabled by default, which ensures that any decoding errors are detected when the file is read. If this option is
disabled, decoding errors may not be detected until the UDR fields are accessed.

Export Saves the selected UDRs in an ASCII file, which may be opened in any text editor. All UDRs are displayed as rows, where the
fields are separated by any of the following:

Comma
Semicolon
Tab

Non-existing fields are marked [null]. The first line will always hold the field names.

Print Prints a summary table based on the current UDR selection (the Table option). It is also possible to get a detailed printout (the
Details option). In the latter case the UDRs will be presented the way they appear when you click on the Edit button.

Edit Menu
Item Description

Bulk Opens a new dialog from which a group of selected UDRs can be edited simultaneously. For further information, see 3.2
Edit... Editing a Bulk of UDRs.

UDR A list from which a UDR view may be selected. A UDR view is a user defined filter which displays additional columns
View containing UDR field data. If(None) is selected, the list will consist of two columns only: Pos and Type. However, if a file is
opened, a third column Orig Pos will become visible as well. For further information, see 4. UDR Views.

13
3.1 Editing a UDR
Double-clicking a UDR will result in a new dialog, containing the complete UDR which allows the contained data to be edited.

Edit UDR dialog

Item Description

Name The name of the UDR fields. A black field (MANDATORY) can be edited, however, Present cannot be disabled. A red field
(OPTIONAL) can be edited as well as set to Present. Note that removing a field containing sub-fields and then creating it
again will not recreate the sub-fields. The same applies to elements in a list field.

The special field OriginalData holds the raw UDR input data.

Value Holds the field values. To edit a value, select the value and enter the new value.

Present Shows whether the fields are present in the UDR View or not

Show Only displays present fields (as specified by the Present flag)
Only
Present

Advanced Edit - List and Update UDR Fields


It is also possible to add and remove elements from fields of list type, as well as to create UDRs for fields of UDR type.

Place the pointer on the Name column and right-click to select either Add Entry, Remove Entry... or Create UDR.

Place the pointer on the Value column and right-click to select Advanced Edit....

14
Edit UDR dialog - editing byte array fields

Setting Description

Null Sets the field value to NULL

Advanced Opens up a new dialog, where the field value may be changed. To append values, click on the subsequent byte and type in
Edit... the value. In this case (byte array) only hexadecimal digits are allowed.

Advanced Edit dialog

3.2 Editing a Bulk of UDRs


Other than editing UDRs one at a time, it is also possible to edit a group of UDRs at once. Select the UDRs of interest, or none (this way all
UDRs within the file are considered) and select Bulk Edit... from the Edit menu.

15
The Bulk Editor

Setting Description

UDR A list from which a UDR type can be selected.


Type

Auto Edit This mode allows the editing of a bulk of UDRs using matchers and assignments. For further information, see the section
below, Bulk Edit - Auto Edit Mode.

Manual Selecting this feature will disable both the Matchers and Assignments, and allow the user to write custom APL code in the
APL Edit APL View that becomes visible below. For further information, see the section below, Bulk Edit - Manual APL Edit.

Activation This section shows the Target UDR Count and an Edit button.

If you click on the Edit button, the Bulk Edit Result dialog will open, if the configuration is valid, showing the type of changes
performed.

Bulk Edit - Auto Edit Mode


Setting Description

Matchers Defines the UDRs targeted for update. For further information about adding Matchers, see the section below, Add
Matcher.

Join Style Indicates if All or Any will apply to the conditions in the Matchers list

Field Specifies the fields to examine when defining target UDRs

Matcher The expression to apply when matching.


Configuration
Ericsson IOG/IN formats: (see 8. External - Ericsson IOG/IN Records). This special format type is described
through the inwvariant of the external block.

Assignments Defines the changes to be made to the targeted UDRs. For further information about adding Assignments, see the
section below, Add Assignment.

Field Fields to edit

Assignment The new value of the field


Configuration

Add Matcher
A Matcher is added by selecting Add in the Matchers section. The following dialog is displayed:

Add Field Matching dialog

16
Setting Description

UDR Field List of available fields to use as matchers. Depending on the field type, the Matcher Type list updates to display valid
choices.

Matcher Defines the check criterion for the field. The content of the list varies depending on the field type. Supported types and
Type some of their possible matchers are:

Alphanumeric (string, char) - Contains, Begins With, Matches RegExp etc.


Date - Equals, Before, After etc.
Numeric - Not Equals, Greater Than, Less Than etc.
Bytearray - Is null, Not null, Length equals, Length not equals, Byte at position equals

Parameter Some field types can consist of several parts. For instance, date types can contain separate values for year, month, and
day. Regular numeric/alphanumeric fields will have Value stated.

Is APL Indicates the possibility to enter variable values for the validation field via the use of APL code in the Value column. This
feature makes it possible to refer to other fields within the same UDR (as well as call to APL functions) during processing.
In the image below, the P155_PackageLength field is of type int.

If Is APL is disabled, the Matcher Configuration is interpreted as an int instead of a function.

Add Matcher

Value The value of the field. By default, a constant is entered. If Is APL is selected, APL syntax can be used to create a variable
definition.

Add Assignment
An Assignment is added by selecting Add in the Assignments section. The following dialog is displayed:

Add Field Assignment dialog

17
Setting Description

UDR Field List of available fields for assignment. Depending on the field type, the Assignment Type list will update to display valid
choices.

Assignment Defines the changes to be made to the field. The content of the list varies depending on the field type. Supported types
Type and some of their possible assignments:

Alphanumeric (string, char) - Assign, Append, Replace, etc.


Date - Assign Current Date, Increase/Decrease Year etc.
Numeric - Assign, Increase etc.
Bytearray - Create, Set at position, Assign, Append, Prepend, Insert, Replace

Parameter Some field types can consist of several parts. For instance, date types can contain separate values for year, month, and
day. Regular numeric/alphanumeric fields will have Value stated.

If the field is a bytearray, a hex editor will open when you click in the Value cell where you can enter the bytearray value
in hex format.

Is APL Indicates the possibility to enter variable values for the validation field via the use of APL code in the Value column. This
feature makes it possible to refer to other fields within the same UDR (as well as call to APL functions) during processing.

Value The value of the field. By default, a constant is entered. If Is APL is selected, APL syntax can be used to create a variable
definition.

Bulk Edit - Manual APL Edit


This mode allows the user to write custom APL code in the APL View that becomes visible below.

Manual APL edit

Note!

When editing UDRs in manual mode, the changes of the UDRs will not be traceable. All UDRs in the batch will be updated,
although their values may not be changed.

18
4. UDR Views
From the UDR View Editor, it is possible to define filters to control how UDR data is listed and presented in the UDR File Editor main
window and the Aggregation Session Inspector.

UDR File Editor with no UDR View applied

Applying a view will add the fields of the view as columns. A UDR View does not have to be applicable on all UDR types within a displayed
file. If a field does not exist for a particular UDR, no such field is displayed.

UDR File Editor with a UDR View

The UDR View Editor is found in the UDR File Editor, which is opened by clicking the Tools button in the upper left part of the
MediationZone Desktop window, and then selecting UDR File Editor from the menu.

19
UDR View Editor

Setting Description

UDR Type A list from which a UDR type is selected

UDR Fields Displays the selected fields that builds the view

20
5. Ultra Format Converter
When an Ultra Format Definition is updated, the MediationZone system by default automatically updates persistent data when it is
accessed, in order to reflect changes. The automatic conversion can either be completely disabled, or enabled only for a selection of
formats. It is also possible to set default values for added fields. The access group is permitted to launch and maintain the data in the Ultra
Format Converter.

The following list of MediationZone applications shows agents able to handle persistent data, and how they handle historic (outdated)
UDRs when the automatic conversion is enabled (or the specific format is part of the selection in the Ultra Format Converter).

Application Handling

Database Collected UDRs are updated.


Collection Agent

Decoder (using Processed UDRs are updated.


MZ Tagged)

Aggregation All UDRs belonging to the same format definition - historic and updated - will be considered valid for comparison.
Sessions
If historic UDRs exist within old sessions, or the session format itself has been updated, the session and its
content will be updated the next time it is accessed.

ECS UDRs will be updated whenever they are read from ECS by the ECS collection agent or accessed through the
ECS Inspector.

Note!

Attempts to collect, process or update historic UDR data when the automatic conversion for a specific format is disabled causes
the workflow to abort (or an exception, when done outside workflow processing).

Conversion Rules
Fields removed from the format definition are deleted and cannot be retrieved. Added fields are by default either set to 0 (zero) (numeric
types), false (boolean types), or null (all other types). Changing the type of existing fields may cause the field content to be deleted.
For instance, changing from string to int sets the field to 0 (zero), provided that no valid default value is defined.

Only numerical types (int, long, etc) are directly interchangeable. If a numeric value exceeds the number of bytes allowed in the new
type, it will be truncated.

Disabling Automatic Conversion


It is possible to disable the automatic conversion to new format versions, and instead define default values for UDR fields added in the new
version. This is done from the Ultra Format Converter which is accessed by clicking the Tools button in the upper left part of the
MediationZone Desktop window, and then selecting Ultra Format Converter from the menu.

21
Ultra Format Converter

Setting Description

Convert Indicates the UDR types for which the conversion is enabled. There are two options:

Selected Only - Only the formats listed in this window will be considered.
All - All formats will be considered, regardless of the types listed.

UDR A list of the formats included as valid for conversion (provided that Selected Only is the selected Convert option).
Types

Default Selecting a UDR Type will activate this option.


Values
If fields have been added to the format, or the type of an existing field is changed, default values can be defined. The latter
case is especially useful since the old field values are reset if the types are not interchangeable.

Note!

It is not possible to assign values to old, already existing fields (it can be configured, however will have no effect
upon update.)

22
6. Introduction to the Ultra Format Definition
Language
Ultra Format Definition Language (UFDL) is the language used to configure the MediationZone format handling subsystem. It describes the
physical structure of incoming and outgoing (external) data, internal (working) formats, as well as decoding and encoding rules.

The agents responsible for translations are the Decoder and Encoder agents. The Decoder converts external raw data (byte arrays) into
internal data (UDRs). The Encoder works the other way around, converting internal formats into raw output data for the forwarding agents.

Decoders and Encoders in MediationZone processing data

UFDL syntax is entered in the Ultra Format Editor which is opened by clicking the New Configuration button in the upper left part of the
MediationZone Desktop window, and then selecting Ultra Format from the menu.

6.1 Building Blocks


UFDL uses the following building blocks:

Block Description

External An external format describes the physical layout of the raw data. It can be declared in multiple ways such as through an
formats asn_block (for BER or PER encoded formats) or an external block.

Internal An internal format describes the structure of the internal, working UDR type. These formats are used in workflow activities
formats (such as APL processing) and are declared through an internal block or generated from an automatic in-map (through a
target_internal specification).

In-map A mapping from an external type to an internal type. This is the basic component used to describe a decoder, and it is
declared through the in_map specification.

Note!

Mapping between different Ultra formats may work, but is not supported, for example, between xml format and
ASN.1 formats. Instead you need to add an Analysis agent to execute the conversion.

Out-map A mapping from an internal type to an external type. This is the basic component used to describe an encoder, and it is
declared through the out_map specification.

Note!

Mapping between different Ultra formats may work, but is not supported, for example, between xml format and
ASN.1 formats. Instead you need to add an Analysis agent to execute the conversion.

Decoder A decoder uses one or more in-maps to specify a set of decoding rules to be used when decoding input data. Blocking
specification as well as other file structure specifications can also be added.

Encoder An encoder uses one or more out-maps to specify a set of encoding rules to be used when decoding input data. Blocking
specifications can also be added.

23
As an example, consider a incoming call detail record containing information on the type of call, who made the call and which number was
dialed. That means that the record contains three fields. Two fields and new termination marks (;) for each field in the record in order to
conform to the expected output format must be added.

Concerning both incoming and outgoing external formats, each record (the last field in the record) is terminated by a newline character.

An example of two external format definitions

As can be seen in the image above, An example of two external format definitions, an external specification is used to describe the raw
data formats.

The UDR format used by the processing agents is generated through the target_internal specification. This format is automatically
generated based on the incoming external data and includes any fields in the internal specification of the in-map.

In this case, the generated internal format will consist of the fields from the external incoming format with two additional fields. Hence, we
introduce an internal containing the new fields only.

Relation between declared and generated internal formats

Finally, the in_map and out_map definitions are used when creating decoder and encoder definitions to be used in MediationZone.

UFDL code of the example is described as follows:

24
external recA {
int type : static_size(1);
ascii Anum : static_size(8);
ascii Bnum : terminated_by(0xA);
};

internal recA_int {
int field1;
int field2;
};

external recB {
int type : terminated_by(";");
ascii Anum : terminated_by(";");
ascii Bnum : terminated_by(";");
int field1 : terminated_by(";");
int field2 : terminated_by(0xA);
};

in_map A_map : external(recA),


internal(recA_int),
target_internal(recA_TI) {
automatic;
};

decoder decode_A : in_map(A_map);

out_map B_map : internal(recA_TI),


external(recB) {
automatic;
};
encoder encode_B : out_map(B_map);

External
Network elements delivering data to MediationZone produce records in various physical formats. An external format describes the physical
structure of the incoming data, as well as the data delivered by the MediationZone system. It can be any type of ASCII, XML or binary data.
Typically, one external block per record type is defined. File headers and trailers are also considered being records.

The external formats are described through a number of different methods with different syntax. The external formats, currently supported
by the MediationZone Platform, are:

Sequential formats: ASCII or binary based on static or dynamic sizes for both records and fields (see 7. External - Sequential
Format). The generic external specifications are defined via the external block.
Ericsson IOG/IN formats: (see 8. External - Ericsson IOG/IN Records). This special format type is described through the inw
variant of the external block.
ASN.1 based formats: A subset of ASN.1 is supported (see 9. External - ASN.1 Formats). These formats can be used to specify
either BER or PER encodings.
XML formats: A subset of the standard XML schema syntax is supported and can be used to handle different XML formats. For
further information, see 18. XML Schema Support.

Records of different format types may exist in the same record. An example of this could be a BER (ASN.1) record that contains a field
whose format is a sequential record (specified through an external block). The image below, A definition of a sequential external record,
illustrates the syntax for a sequential record type.

A definition of a sequential external record

25
Internal and In-Map
There are two ways to define an internal UDR format in UFDL. Either by using the automatic generation through an in_map specification,
or through an internal declaration block. For more complex formats it is often impractical to manually write an internal format if all that is
desired is a direct mapping from the fields in the external format. In these cases the target_internal specification in the in_map is
used. An additional internal block can be specified in the in_map if additional processing fields are needed.

The in_map manages the mapping of external data into the internal UDR structure. An external must be defined, and one or both of the
internal and target_internal options must be used to specify the internal format to map to.

For information about how to specify an internal format, see 10. Internal Formats. For information about in_map specifications, see 11.
In-maps.

Example - An internal type declaration

internal AXE_DAM {
string ANumberAreaCode;
string ANumberClass : optional;
string BR_IncomingPQR;
string BR_NumberClass;
intLen ANumber;
intLen BRNumber;
intLen BSNumber;
string PreProcRecordClass;
};

An internal type declared through target_internal will automatically be generated based on the fields of the external and internal
formats.

The target_internal is the format used by the processing agents in MediationZone. It can be a direct representation of the external
incoming format, or a combination of the external and additional fields from an internal definition. An internal definition is needed in case
any changes to the default mapping from the external format are required or if several externals are mapped to the same UDR.

To understand the structure of the generated target_internal format, it is necessary to understand how the automatic mapping of an
in_map is performed. When automatically mapping an external format (that is, using the automatic keyword), all external fields are
mapped to an internal field unless the external format specifies a different automatic mapping behavior (for example through the
external_only keyword).

The automatic in-map generation will perform the following steps for each external field:

1. If the field has an explicit mapping specified, use that mapping (explicit field mapping specifications overrides automatic mapping).
2. If there is a field in the internal format with the same name as in the external, the external format field is mapped to the
internal field.
3. Otherwise, a new field is created in the generated internal format with the same name as the external field, and a type is chosen
according to the external format (the default mapping type).

If the automatic mapping algorithm requires new fields, a new internal type is first created. This type will get the name specified in the
target_internal, and is a subtype of the internal type (if specified). Decoders using this in_map will then produce records of that
internal type.

The external field default mapping type depends on the external format specification and if the external field is of a constructed type (a list
or record type), the automatic mapping logic will be applied recursively to any sub-formats.

Other than automatic mappings, it is possible to specify explicit type mappings, where the corresponding external and internal fields are
specified directly.

26
A resulting target_internal

In the image above, a resulting target_internal, a combination of explicit (the e: and i: specifications) and automatic mapping is
used to create the resulting target_internal CDR_TI.

Event Types
Event format types are special internal types that can be sent to the MediationZone Event Server. Events store additional internal
information used during the event processing. For performance reasons, and in order to avoid confusion, event types should only be used
as “message UDRs” dispatched to the Event Server. In other words, do not use event UDRs to transport ordinary UDR data.

Event types are declared in the same way as internal types, except for the keyword event, which is used instead of internal. For
further information, see 10. Internal Formats.

The following code will result in the events shown in the image below, Event format showing added fields:

event AMA_Event {

int anum;
int bnum;
...
}

Event format showing added fields

For further information about Event handling, see the Desktop User's Guide.

27
Decoder
A Decoder defines what external records to accept, and describes any additional file structure information, such as whether file blocking is
used in the input data.

A decoder definition consists of in_maps or other decoders. The most basic decoder definition uses the single in-map argument. In the
following example, myDecoder will appear in the configuration of a Decoder agent:

Example - Decoder

The decoder myDecoder is created by taking one in-map. The incoming data is blocked in 2048 bytes, with the block padding
0x00.

decoder myDecoder : in_map( my_in_map ),


block_size(2048),
terminated_by(0x00);

A decoder definition can consist of several in-maps, that is, the data stream received may contain several record type definitions. Another
possible definition is combining several decoders, called constructed decoder .

The image below, Two ways of defining a decoder, introduces some validation logic to the incoming stream of data.

Two ways of defining a decoder

The first case, TTFile1, will require a batch to start with a header, end with a trailer, and contain any number of UDRs in between. The
second decoder definition (TTFile2) ignores the order and occurrence of arriving records. For further information about decoder
specifications, see 12. Decoders.

Out-map
An out-map defines how the internal data will be mapped to the outgoing format. It requires an internal and an external definition as
arguments and maps fields either automatically or through explicit field mappings.

The minimum required to define an out-map

In the image above, The minimum required to define an out-map, it is shown how an external Access_Reject_Ext is created using
the internal Access_Reject_Int. The automatic keyword map all fields with the same name, while field names present in the
internal, but not the external, are ignored. For further details about how to specify an out_map see 13. Out-maps.

Encoder
Encoders define the rules for mapping internal UDRs to external data structures (raw data).

An encoder must be defined in order to make any out_map usable, that is, selectable from the dialog. The most basic definition will have
one out_map as its only argument. In the following example, myEncoder will appear in the configuration dialog of an Encoder agent:

28
Example - Encoder

encoder myEncoder : out_map( my_out_map );

Similar to a decoder, an encoder takes one or several out-maps to define the structure of the outgoing data. For detailed information about
encoder specifications, see 14. Encoders.

6.2 The Ultra Module


A saved format will be referred to by the qualified name under which it was saved (its module name). In APL or UFDL, it can be referred to
by the module name followed by a dot and the name of the internal definition. Alternatively import statements can be used to avoid the
necessity of always specifying the module name.

The import Statement (APL Code)

In Analysis and Aggregation agents, module names are not needed if the format is selected in the UDR Types list in the configuration
dialog of the agent. Also, the APL Editor allows selection of the UDR types from a list. Right-click in the Code Area to display the UDR
Assistance... via which the UDR Internal Format Browser can be opened.

If none of these lists are used, the full format name must be referred to, or the import keyword must be used. The module name must be
prefixed with ultra.to distinguish format imports from APL code imports.

Example - Referring to UDR Types in the APL Editor

The module in this example is named Default.CDR, and contains the target_internal CDR_TI which is to be used in a
function created in the APL Editor.

Case 1 - import statement

import ultra.Default.CDR;

CDR_TI newUDR() {
CDR_TI myUDR = udrCreate( CDR_TI );
myUDR.field1 = 22;
myUDR.field2 = 55;
return myUDR;
}

Case 2 - no import

Default.CDR.CDR_TI newUDR() {
Default.CDR.CDR_TI myUDR = udrCreate( Default.CDR.CDR_TI );
myUDR.field1 = 22;
myUDR.field2 = 55;
return myUDR;
}

The import Statement (UFDL Code)


It is possible to split different blocks of a format between several format definitions. In these cases, the import statement may be used to
avoid referring to the module name. The ultra. prefix is not needed (or allowed) in the UFDL import statement.

29
Using import in UFDL Code

Suppose there is a format definition with the module name Default.AMA containing an internal myInt, and a new format definition must
include the internal in the in_map. This can be accomplished in two ways:

Case 1 - import

import Default.AMA;
in_map exMap : external( myCDR ),
internal( myInt ),
target_internal( myCDR_TI ) {
automatic;
};

Case 2 - no import

in_map exMap : external( myCDR ),


internal( Default.AMA.myInt ),
target_internal( myCDR_TI ) {
automatic;
};

Note!

Referring to ASN.1 structures from outside the format definition will require the ASN.1 module (if defined) to be part of
the reference.

When invoking the import ultra. function from within the folder that contains the UFDL file, you do not need to
specify the folder name.

Name Lookup Rules


Consider the following ASN.1 block example. Depending on from where a format name is referred, the name lookup will work differently.

References from within the asn_block specification:

If there is a name in the asn_block specification, choose that name.


If there is a name in the same format definition, choose that name.
Evaluate the import statements.

References from within the same module but outside of the asn_block:

If there is a name in the same format definition (outside of any asn_block specifications), choose that name.
If there is a name in the asn_block specification, choose that name.
Evaluate the import statements.

Example - Shortened ASN.1 Block

asn_block {
exchangeRec DEFINITIONS IMPLICIT TAGS ::=
BEGIN
main_udr ::= SEQUENCE
// Field specifications
};

30
Note!

References to existing ASN.1 formats in UFDL requires the ASN.1 module to be part of the reference. Suppose the previous
external specification, saved under the name Default.myASNformat, is to be included in a new in_map:

Case 1 - import

import Default.myASNformat.exchangeRec;

in_map exMap : external( main_udr ),


internal( myInt ),
target_internal( myCDR_TI ) {
automatic;
};

Case 2 - no import

in_map exMap : external( Default.myASNformat.


exchangeRec.main_udr ),
internal( myInt ),
target_internal( myCDR_TI ) {
automatic;
};

31
7. External - Sequential Format
A sequential external format specification consists of two parts; the record declaration and the field declarations. The record declaration
can contain information about UDR size and how the record type is identified. The field declarations may be grouped into larger structures
such as bit_block and set. The syntax of the external format declarations is declared as follows:

external <format_name> [: <record declaration>] {


<content declarations>
};

The comma separated elements of the record declaration list may be (in any order):

terminated_by( <terminator> )
dynamic_size( <expression> )
static_size( <constant expression> )
identified_by( <conditional expression> )

The content declarations can be:

field declarations
bit_block declarations
set declarations
switched_set declarations

7.1 Record Declaration


To be able to split the incoming data stream into individual records the record size must be calculated. The record size calculation is
managed by the record options terminated_by, static_size and dynamic_size, and includes the termination character. The
different ways to calculate the size are tried in the following order:

1. If this record is a sub-record (a field of another record) and the parent external field size is specified, that size is used. How field
sizes are calculated depends on the external type of the parent record type. A typical case appears if an ASN.1/BER type
contains a sequential subtype, in which case the BER size specification is used.
2. If the static_size option is specified, that size is used.
3. If the dynamic_size option is specified, that value is calculated and used.
4. If the terminated_by option is specified, the input stream is scanned until the terminator is found (this implies that no field can
contain this terminator since the first occurrence will be seen as the end of the UDR). The record size includes the termination
character but will never take up more than the total remaining size in the UDR.
5. If everything else fails, an attempt to calculate the size is made by summarizing the total size of all content declarations.

terminated_by
The function terminated_by identifies how a record is terminated. Records can be terminated by a byte constant or EOF (end of file).
The constant can be declared numerically (decimal or hexadecimal), or as a plain ASCII char. In the latter case, the literal must be
enclosed in quotes.

Note!

Only one byte is allowed.

32
Example - Using terminated_by

Record termination by constant:

external myExternal : terminated_by(0xA) {


// <contents declaration>
};
external myExternal : terminated_by(';') {
// <contents declaration>
};

Record termination by end of file:

external demoExt : terminated_by(EOF) {


// <contents declaration>
ascii remainingData : dynamic_size( remaining_size );
};

dynamic_size
Binary encodings that vary in size often contain a field with information on their specific length. In these cases, the dynamic_size
declaration may be used, specifying how to calculate the record size from the values of one or more fields.

Example - dynamic_size

external myExternal : dynamic_size(recordLength) {


int RecordLength : static_size(2);
// <continued contents declaration>
};

static_size
Records of fixed size are declared using static_size.

Example - static_size

external myExternal : static_size(<recordLength>) {


// <contents declaration>
};

identified_by
If the identified_by option is present, it specifies the condition under which a record of this type is present in the stream. This is used
when the input data contains different record types, in which case the decoder uses the identified_by expression to evaluate if the
data matches the record type.

The condition is also evaluated when deciding whether to use a specific out map during encoding.

33
Example - identified_by

external myUDRType1 : identified_by( udrType == 1 ) {


int udrType : static_size(1);
// <continued contents declaration>
};

7.2 Field Declarations


The syntax for fields of a sequential record, record field declarations , are declared as follows:

<field_type> <field_name> : <field options> ;

Primitive Field Types


The following primitive types are supported:

34
Primitive Description
Field Type

ascii ASCII encoded string. This type may also be used for other types of string encodings with the character_encoding
option. The default encoding used is ISO8859-1.

asn_length Special type used to encode a BER encoded size specification. It decodes to the BER length specification as well as
the length of the length specification itself. The type makes it possible to decode some special cases of BER encoded
data without using ASN.1 format specifications. The special option content_only could also be used to only get the
BER length specification.

bcd Array of digits encoded in BCD. Nibble order can be specified as bcd(msn_fd) or bcd(lsn_fd). The msn_fd
means that the most significant nibble is the first digit, while lsn_fd uses the least significant nibble as first digit. The
msn_fd is default.

bytearray Byte array.

ebcdic A special case of ascii type, equivalent to ascii: character_encoding("Cp1047"). All options available for
ascii fields are also available for ebcdic fields.

float types ( Binary encoded float value. This type supports IEEE754 standard 32-bit and 64-bit data encodings. The only
float, difference between float and double is that the field is automatically mapped to their corresponding internal type.
double)

integer types ( Binary coded integer value. The first byte is the most significant (that is, big endian order). The field can be used with
byte , short the field options unsigned or signed to specify if the data will be decoded unsigned (which is the default) or two-
, int , long , complement signed. The byte order can also be explicitly specified by, for instance, using int(little_endian) or
bigint ) int(big_endian) as the type name.

The only difference between the types, when using an automatic in_map, is that the field is automatically mapped to
their corresponding internal type.

Note!

Since the integer types are handled internally as fixed-length signed integers (except for bigint), there can
be overflows in both decoding and encoding. If this occurs the integer values are truncated.

msp_length Special type used to decode the length field in a Siemens MSP billing event. The length field specifies the event length
excluding the length field itself.

external MSP_BILLING_EVENT
{
msp_length l : external_only;
ascii v : dynamic_size(l);
};

list Type that can be used to decode a list of elements.

external MySubUDR {
int dataLength : static_size(1);
ascii secretData : dynamic_size(dataLength);
};
external MyUDR {
int elementCount : terminated_by(":");
list<MySubUDR> myList : element_count(elementCount);
int userType : terminated_by(0xA);
};

The list can have any of the following field size options static_size, dynamic_size, terminated_by or
element_count. For further information, see the section below, Field Size Specifications

35
Primitive Field Options
Primitive Field Description
Option

character_encoding Specifies that the field is encoded with the encoding named <encoding_name>, using the standard Java
(<encoding_name>) encoding support.

encode_value Specifies that the encoded value of the field always is <expr>, regardless of whether decoded or set value is
(<expr>) chosen during the processing. This is used for encoding only.

float, double Informs the decoder that the value is actually a float value specified as a string and that the automatic
mapping of the field is the internal type float or double. Only applicable for ascii fields.

int(base10), int Informs the decoder that the value is actually an integer of decimal or hexadecimal base and that the
(base16) automatic mapping of the field is of the internal type int. The integer types (byte, short, long, bigint)
can be used instead of int, in which case automatic mapping is done to the specified type. Only applicable
for ascii and bcd fields.

lsb The least significant bit of the field is <int_constant>. Value range is zero (0) to seven (7) (both inclusive).
(<int_constant>) Only applicable for size one (1) integer type fields. This option is typically used together with the lsb option.

msb The most significant bit of the field is <int_constant>. Value range is zero (0) to seven (7) (both inclusive).
(<int_constant>) Only applicable for size one (1) integer type fields. This option is typically used together with the lsb option.

native_size Specifies the number of BCD digits for a bcd declared field. This size does not cover field size calculation and
(<expr>) dynamic_size must generally also be specified.

byte_alignment Specifies that a field begins at the next even multiple of an alignment byte size. The value must be an even
(<int_constant>) power of 2 (for example 1, 2, 4, or 8). This field option can also be used in a bit_block or repeat_block .
For an example of how this option can be used, see the section below, Bit Blocks.

Note!

The byte_alignment field option is used for decoding only, and counts from the start of the UDR.

Field Size Specifications

Field Size Specification Description

static_size(<constant>) Used to specify a static size of a field (in bytes)

dynamic_size(<expr>) Used to specify a dynamic size of a field (in bytes)

element_count(<expr>) Used to specify the size of a list field (in number of elements)

terminated_by Used to specify a dynamic field terminated by a specific constant


(<constant>)

bit_size(<expr>) Used to specify a size of a field (in bits). This size specification can only be used inside a
bit_block.

padded_with(<constant>) Used to specify padding character

align(left) Specifies that the field is left aligned, default

align(right) Specifies that the field is right aligned

When decoding a field, the size calculation is done in two steps. First the occupying size is calculated. This is the required field size in the
record. After that the core size and offset is calculated, which is the part of the field actually decoded into the internal field.

The occupying size is calculated as follows:

1. If static_size is speci fied, this one is used.

36
2. If dynamic_size is specified, then this one is used.

3. If element_count is specified, then this one is used.

4. If terminated_by is specified, then this one is used. The field size includes the termination character but will never take up
more than the total remaining size in the UDR. (The reason that this is not considered as a decoding error is to support the
trailing_optional field option).

5. Otherwise (if the field type supports it) the field size will be deduced directly from the type. This is supported by constructed types
(sub-records) and the asn_length primitive type.

The core field data always has the full occupying size for constructed fields (record fields). For primitive fields the size is specified as
follows:

1. For a BCD field, with native_size specified, this along with the alignment specification is used.

2. If terminated_by is used to find the occupying size, this terminator char (or nibble for BCD) is removed.

3. Any padding is removed (while considering the alignment specification). The padding is either specified with padded_with or
with terminated_by providing the occupying size is not calculated using the terminator (this case is present due to historical
reasons and in current versions padded_with should be used instead). If the field is an ASCII field, space is used as default
padding.

Field Options for Optional Fields


The following field options are used to specify when a field is present.

Field Option Description

present if The field is present if the <condition> evaluates to true.


(<condition>)

trailing_optional The field is present unless the end of the UDR data has been reached. This is a convenient option equivalent
to present if(remaining_size >0).

Other Field Options


Field Option Description

external_only The field will not be automatically created in the target_internal when performing automatic mapping. This is
useful for fields containing “decoding logic” and provide no useful information after decoding. Typical examples
could be recordLength and recordType fields.

udr_size and remaining_size


Fields may need to use the size of the containing record in expressions. This is done by using the udr_size keyword.

Example - udr_size

external SimpleSequential {
int recordType : static_size(1);
ascii secretData : dynamic_size(udr_size-1);
};

37
In the previous example, the size of SimpleSequential is unknown at declaration time. However, when a size is provided (specified in a
parent record type), the secretData field will occupy this entire space minus one byte (which is used by the recordType field in this
example).

Note!

If the size is not supplied by a parent record, the record size calculation rules will result in an undefined size since the udr_size
value is unavailable before the size has been calculated. This would cause a decoding error.

The other special value that depends on the record size is remaining_size, which is the size remaining until the end of the record. The
previous example could have been written using remaining_size instead of udr_size, and is shown in the following example.

Example - remaining_size

external SimpleSequential {
int recordType : static_size(1);
ascii secretData : dynamic_size(remaining_size);
};

Bit Blocks
Bit blocks are used when the data record contains fields that are not byte aligned. When declaring fields in bit blocks there are two ways to
specify which bits to use for the field content. When using a bit_block of a single byte, it is possible to specify the most and least
significant bit of the field using msb and lsb, as previously described. The alternative is to use the bit_size option to specify the number
of bits spanned by the field.

You can also use the byte_alignment field option if you need to specify from which byte a field begins. This field option can only be
used for decoding. For further information on byte_alignment, see the section above, Primitive Field Types.

The general syntax of the bit blocks is as follows:

bit_block : <size specification>


[, present if(<cond>) ] {
<bit_block contents>
};

Example - bit_block with msb and lsb

bit_block : static_size(1) {
int LACLength : msb(7), lsb(4);
int OwnerIDLength : msb(3), lsb(0);
};

Example - bit_block with bit_size

bit_block
int hour : bit_size(5);
int minute: bit_size(6);
int second: bit_size(6);
int eventId: bit_size(3);
};

Example - bit_block with byte_alignment

38
This example shows how the byte_alignment field option can be used in a bit_block , in which the secondBit field
begins in the last byte in a bit_block of five bytes:

external BitBlock_ByteAlignment {
bit_block : static_size(5) {
byte firstBit: bit_size(1);
byte secondBit: bit_size(1), byte_alignment(4);
};
};

Except for simple fields, a bit_block can contain repeat_block constructs in the contents part. For a description of repeat_block
see the section below, Repeat Blocks.

Repeat Blocks
A repeat_block can be used to specify that a group of fields is to be repeated a specified number of times. Currently this construct can
only be used inside bit_block structures or another repeat_block structure. However this is restricted to a maximum of two levels of
repeat_block. See the example below.

You can also use the byte_alignment field option if you need to specify from which byte a field begins. This field option can only be
used for decoding. For further information on byte_alignment, see the section above, Primitive Field Types.

The general syntax of the repeat blocks is as follows:

repeat_block ( <repeat count> ) {


<repeat_block fields>
};

Example - repeat_block

external BitBlockTest {
bit_block : dynamic_size(remaining_size){
int string_count: bit_size(8);
repeat_block(string_count) {
int string_length: bit_size(8);
repeat_block(string_length) {
int character: bit_size(8);
};
};
};
};

In the UDR Internal Format Browser, the structure of the UDR Type for this example appears as shown below:

39
Note!

It is not possible to encode to a structure containing a repeat_block .

Constructed Types
A sequential field can be a type that is an instance of another external format.

Example - Constructed types

external MyParentFormat {
int field1 : static_size(4);
MyEnclosedFormat field1;
};

Here MyEnclosedFormat can be any external format.

set Construct
The set construct is used for decoding formats containing optional blocks of additional data. The syntax of the set Construct is declared as
follows:

Example - set Construct

external MyFormat:
dynamic_size(recordSize) {
int recordSize: static_size(4);
set : dynamic_size( remaining_size ) {
MyPackage1 package1: optional;
MyPackage2 package2: optional;
list<MyPackage3> package3;
};
};

All the formats, MyPackage1-3, must be declared with the identified_by option. The optional packages may appear in any order in
the input file, however it is confirmed they do not appear more than once. Currently all fields in a set construct must be declared optional.

40
If the field type in the set is a list type, the set may contain multiple records of the list element type. The list type fields are not optional.
Instead, when no matching records are found, the list is empty.

If a size is not specified on the set level, Ultra cannot validate that all the data in the UDR has been decoded. The user is therefore
recommended to specify the size, unless the set size in advance is unknown (for instance if the record is terminated by a terminator
package or the set size calculation is needed for the record size calculation). The dynamic_size(remaining_size) specification used
in the previous example is often correct.

switched_set Construct
The switched_set construct can often be used instead of the set construct. It has advantages (in performance and in ease of usage)
especially when the separate sub-packages are simple. The syntax is however more complex compared to the basic set construct. The
syntax of the switched_set construct is declared as follows:

switched_set( <switch field> )


[: <size specification> ] {
<prefix fields>
<switch cases>
[<default case>]
};

The size specification is allowed to contain normal size options. The other parts of the declaration are the prefix fields, decoded for each
package in the set and the prefix fields. All the prefixes must have static sizes. The switch field must be one of the prefix fields.The syntax
of the switch case is declared as follows:

case( <case value> ) [: include_prefix] {


<case fields>
};

The case fields are normal field specifications with the additional possibility of declaring list fields for the case where a package can be
present repeatedly. If include_prefix is specified, then the case body will be decoded including the prefix fields. The syntax of the
default case is declared as follows:

default [: include_prefix] {
<case fields>
};

The decoding of a switched_set is performed according to the following steps:

1. Decode the prefix fields.

2. Decode the case matching the value of the switch field. If no case matches, decode the default case. If there is no default case,
end the switched_set decoding.

3. Repeat steps 1-2 until the switched_set size (or the end of the UDR) has been reached.

Example - Format with a switched_set:

external SwitchedSetExample: terminated_by(0xA) {


// Size is remaining_size -1 (minus the terminator linefeed)
switched_set( packageId ):
dynamic_size( remaining_size - 1 ) {
ascii packageId: int(base10), static_size(1);
ascii packageLength: static_size(1), int(base10),
encode_value( case_size - 2 );
case(1) {
list<ascii> list1:
dynamic_size( packageLength );
};
case(2): include_prefix {
ascii packageId_3: int(base10), static_size(1),
encode_value(3), external_only;
ascii packageLength_3: int(base10), static_size(1),
encode_value(case_size - 2), external_only;
ascii body_3: dynamic_size( packageLength_3 );

41
};
default: include_prefix {
list<ascii> defaultContent:
dynamic_size( packageLength + 2 );
};
};
};

Encoding Specifications and Expressions


To support encoding to binary formats, it is often necessary to explicitly specify which value to be encoded in the external fields. Normally
the value is taken from the corresponding internal field, however there are cases when this is not desirable. For instance, if there is no
mapped internal field (because the external_only option has been used), or the value must be calculated from information about the
encoding (for instance, udr_size). This is done through the encode_value option and there are several special constructs that may be
used in the value expression (see the section above, Primitive Field Types).

1. udr_size - evaluates to the encoded size of the UDR. That means this is not necessarily the same value as during decoding.

2. field_size(fieldName) - evaluates to the encoded size of the named field.

3. field_present(fieldName) - evaluates to true if the named field is present in the encoding. It is always true for non-
optional external fields.

4. case_size - this is only usable within switched_set blocks and evaluates to the encoded size of the current case (including
prefix fields).

If the size expressions are used, the field encoding has to be postponed until the size is known. To be able to do this, Ultra requires that
any such fields are static_size. An example of these concepts is presented next.

Example - Encoding specifications and expressions

external Ext: dynamic_size( udrSize ) {


ascii udrSize: int(base10), static_size(3),
align(right), padded_with("0"),
encode_value( udr_size );
ascii fieldSize: int(base10), static_size(3),
align(right), padded_with("0"),
encode_value( field_present( strField ) ?
field_size( strField ):0 );
ascii strField: dynamic_size( fieldSize ),
present if( fieldSize > 0 );
};

When processing an encode_value instruction, Ultra automatically decides how to convert the value depending on the result type of the
expression. When deciding this, Ultra starts with the default internal type of the external field. If in this case, the type is called
defaultType and the expression type is encodeType, the encoding rules are:

1. If the defaultType is assignable from encodeType, use the default mapping.

2. If the defaultType is string or bytearray and the encodeType is numeric, encode it as a simple ascii value (one byte).

3. If the defaultType is bytearray and the encodeType is string, do standard encoding (ISO-8859-1) of the string.

4. If the encodeType is string and the external base type is ascii (for example using int(base10)), use direct string encoding.

If none of these rules are applicable, the format will not compile. To understand what this means, consider the following field definitions.

Example - Field definitions

ascii strField1: static_size(1), encode_value("10");


ascii strField2: static_size(1), encode_value(10);
ascii intField1: static_size(1), int(base10),

42
encode_value("10");
ascii intField2: static_size(1), int(base10),
encode_value(10);

Expected encoded results for these fields:

Field Expected encoded result

strField1 Both defaultType and encodeType are string. The normal encoding will be used to get the result "1" (since
static_size(1) is used, the result "10" is truncated to one byte).

strField2 defaultType is string and encodeType is byte (numeric). This means that the second rule is applied, and the result
is ascii 10 (newline).

intField1 defaultType is int and encodeType is string. There is no mapping between these types however, since the
external base type is ascii, the string is mapped out as for strField1, and the result is "1".

intField2 defaultType is int and encodeType is byte. Since encodeType is directly assignable to defaultType, it is
mapped out as normal, and the output is again "1".

7.3 Expressions
UFDL may use APL in order to introduce expressions to validate the incoming data. Basically all APL functions are available, except for
plug-ins.

Example - Using expressions

external myRecord :
identified_by( !strStartsWith( myField, "0201" ) ) {
ascii myField : terminated_by(0xA);
};

43
8. External - Ericsson IOG/IN Records
Record Declaration
The syntax of an IN external format is declared as follows:

external <format_name> inw {


<field declarations>
};

No size declarations are necessary for an IN record since it is given by the enclosing sequential record. In fact, an INW record can only be
used as the final field in a sequential record.

Field Declaration
The syntax of an IN field is declared as follows:

bcd tag_id(<int_constant>) <field_name> <: options>;

The available tag_id values are given by the Ericsson IN documentation.

The available options are:

Option Description

num_frame Indicates that the field has a number frame. Two fields will be generated if this is specified, one with the specified
field name and the other suffixed with _NumFrame.

gnci Indicates that the field has a GNCI. Two fields will be generated if this is specified, one with the specified field
name and one suffixed with _GNCI.

gnci on As gnci, however the GNCI is only present if the expression is evaluated to true.
(<expr>)

terminated_by Identical to sequential definitions


(<expr>)

int(base10) Identical to sequential definitions

int(base16) Identical to sequential definitions

44
9. External - ASN.1 Formats
Ultra provides support for parsing a subset of ASN.1 definitions, which can be used to decode from and encode to the corresponding BER
or PER encoded data. ASN.1 parsing is requested in UFDL via the asn_block construct. The syntax of the ASN.1 blocks is declared as
follows:

asn_block {
-- ASN.1 definitions here
};

All ASN.1 constructed types declared either SEQUENCE, SET or CHOICE, will be treated as an external format declaration. The name of
the resulting external format will be the name of the ASN.1 definition. Any ASN.1 module name will be added to the name space (that is,
the total name space for the ASN.1 definition is <folder>.<configuration name>.<ASN.1 module name>).

All occurrences of the dash character (-) in identifiers will be converted to underscores since dashes are not valid in Ultra type naming.

Any in-map or out-map using an external ASN.1 type will by default specify BER encoding. PER encoding can be selected by specifying
one of the map options to PER_aligned or PER_unaligned.

Notes on ASN.1
Inter Module References
It is currently not possible to refer to non-constructed types or list types (that is, SEQUENCE OF and SET OF) declared in other modules.
These must be contained within the asn_block where they are referred to. Any constructed ASN.1 types referred to must be specified in
an ASN.1 IMPORT statement to be available. For instance,

MyType ::= SET(field1 TAC, field2 MobileOriginatedCall)

If TAC and MobileOriginated are declared in another asn_block in the same module,

TAC ::= OCTET STRING;


MobileOriginatedCall ::= SET ( .... )

the following applies:

1. The TAC declaration must be duplicated in the asn_block of MyType.


2. MobileOriginatedCall must be imported within the asn_block containing MyType.

ASN.1 Primitive Type Mapping


ASN.1 types are automatically mapped by Ultra as follows (this applies when there are automatic statements in the in-maps):

ASN.1 Ultra Type mapped to:


Type:

BOOLEAN boolean

bcd string

All ASN.1 string


string types
except
OCTET
STRING

OCTET bytearray
STRING

ENUMERATED int

INTEGER int

REAL float or bigdec

45
By default, the float ultra type will be automatically mapped to the REAL ASN.1 type. Substituting ASN.1 type REAL with
bigdec will cast the field as BigDecimal type.

Mapping to bigdecimal

Another method of mapping a REAL type to BigDecimal, is to use internal.

//Create a flat internal that will be used to populate with integer and bigdecimal
values.
internal flatInternal {
int calledNumber;
bigdec duration;
};

BIT STRING bitset mapping is used. Bitset is mapped to Bit String and vice versa; bitset<->bitstring

For BER BIT STRING encoding: '0410'H is the correct encoding of the bit string '0001'B ("{3}" in APL debug, length of 4
bits.

Note!

The string representation here does not actually give complete information since the length is not included. It
can be inconvenient to have the same string representation for '0001'B and '000100'B, but the reason is that
the same string representation as the Java BitSet class is used. In BER, these values are handled differently.
For example, '0001'B is encoded as 0x0410, while '000100'B is encoded as 0x0210.

For further information about how BIT STRING is encoded/decoded in BER, see ITU-T specification X.690 (the first byte
is not part of the bitstring itself - instead it encodes the number of unused trailing bits in the last byte in the bitstring
encoding, which starts after the first byte).

NULL bytearray (which will have the value null)

Ultra Extensions
Within a UFDL asn_block it is possible to use some extensions which are not part of the ASN.1 standard. These are added to provide
better automatic decoding support for some formats.

Direct BCD Support


A bcd type has been introduced. The ASN.1 formats encoded in BER frequently uses the OCTET STRING to describe BCD data,
leading to complicated processing. By replacing these entries with the bcd type, Ultra will automatically convert such entries. The syntax
for the bcd type declaration is declared as follows:

bcd(lsn_fd)
bcd(msn_fd)
bcd(lsn_fd) terminated_by(<expr>)
bcd(msn_fd) terminated_by(<expr>)

Data Support
Many ASN.1 formats declare date and time information as OCTET STRING . The date type converter has been introduced to manage an
automatic conversion to date instances. A possible syntax of date declaration is declared as follows:

Time ::= OCTET STRING date({HH,mm,ss}) (SIZE(3))


Date ::= OCTET STRING date({yy,MM,dd})(SIZE(3))
Date ::= OCTET STRING date({cc,yy,MM,dd} , {yy,MM,dd})
DateTime ::= OCTET STRING date({cc,yy,MM,dd,HH,mm,ss})

46
Note!

These are applicable on OCTET STRINGS only.

Using Sequential Record Types


Some ASN.1 definitions contain data with an OCTET STRING declaration that contains additional structures. In order to manage this, it is
possible to split such declarations into sequential record types.

It is also possible to use sequential formats to describe constructed ASN.1 types. In this case the tag must be declared as constructed
(a MediationZone specific keyword) to allow Ultra to correctly encode the type.

Example - Simple sequential record type

An example of a simple type, the definition

AddressString ::= OCTET STRING (SIZE(1..20))

within an asn_block (module GSM), can be brought outside the asn_block and be redefined as:

external GSM.AddressString {
bit_block : static_size(1) {
int npi : msb(3), lsb(0);
int ton : msb(7), lsb(4);
};
bcd(msn_fd) msisdn : dynamic_size(udr_size-1),
terminated_by(0xF);
};

Example - Complex sequential record type

An example of a complex type, occurs when a field fieldB is to be decoded differently depending on the value of another field
fieldA. The ASN.1 definition

ComplexType ::= [APPLICATION 1] SEQUENCE {


fieldA INTEGER,
fieldB OCTET STRING
}

could then be replaced with the sequential format

external ComplexType_Seq {
int tagA: static_size(1);
int lengthA: static_size(1);
int fieldA: dynamic_size(lengthA);

// Definitions of SubType1 and SubType2 not included


// in this example.
SubType1 fieldB1: present if( fieldA == 1 );
SubType2 fieldB2: present if( fieldA == 2 );
};

and the extended ASN.1

ComplexType ::= [APPLICATION 1 constructed] ComplexType_Seq

47
bigint Support
Since INTEGER types are automatically mapped to int, which is a 32-bit integer type, INTEGERs longer than 4 bytes will cause decoding
errors. This can be avoided by using the bigint type in place of INTEGER. The only difference between bigint and INTEGER is that
bigint is automatically mapped to the MediationZone bigint type, which can support INTEGERs of any size.

ASN Language Limitations


The ASN.1 compiler is mostly about the type notation of ASN.1. Elements of type notation not supported are:

COMPONENTS OF
WITH COMPONENT
WITH COMPONENTS
ABSENT/PRESENT
ANY, ANY DEFINED BY
ObjectDescriptor
DEFAULT
DEFINITIONS
EXPLICIT, EXPLICIT TAGS
INCLUDES
MACRO
PRIVATE
UTCTime
EXTERNAL
GeneralizedTime
OPERATIONS

There are also limitations regarding the support of value notation or macro notation the only thing supported is the ability to declare
INTEGER constants and use them in constraint specifications.

There is also only limited support for information object classes and OBJECT IDENTIFIER types. Object identifiers will be decoded to
bytearrays and the information object content will only be decoded according to the class definition.

BER Limitations
In addition to the general ASN.1 limitations there are also some limitations regarding BER that must be taken in consideration, which are:

Explicit tags are not supported - All tags are by default implicit (except for tags of CHOICE types, which are always assumed to be
explicit according to the ASN.1 standard). Any attempt to specify explicit tagging will result in a compilation error.
All string fields are encoded/decoded according to ISO8859-1 except for UTF8String.
No validation to ensure that mandatory fields are actually present is performed for SEQUENCE and SET types.
Not all character types are supported. GraphicalString, IA5String, VisibleString, NumericString, and
UTF8String are supported.

PER Limitations
In addition to the general ASN.1 limitations there are also some limitations regarding PER that must be taken in consideration, which are:

For string types, constraints on the permitted alphabet are not handled.
Fragmented encoding (encoding for large size fields) is not supported.
Not all character types are supported. Currently only the GraphicalString, IA5String, VisibleString,
NumericString, and UTF8String are supported.

48
10. Internal Formats
MediationZone uses internal formats to represent data entities that it can process. All processing agents (for instance, Analysis and
Aggregation) work with these internal formats.

A syntax for the internal format is declared as follows:

internal <name> [: (<class specifications> | <format inheritance>) ] {


<field_type> <field_name> [:optional] ;
...
};

The field types may be any of the following:

Field Type Description

any Any type.

bigint Big integer.

bigdec Big decimal.

boolean Boolean.

bytearray Byte array.

byte Integer type (8-bit signed).

char Integer type (16-bit unsigned).

short Integer type (16-bit signed).

int Integer type (32-bit signed).

long Integer type (64-bit signed).

float Float type (32-bit).

double Float type (64-bit).

date Date type, with capability to hold date parts, time parts or both.

bitset A set of bits.

ipaddress An IP address.

drudr An instance of any other internal (all internal are drudr instances).

string String.

field_type can also be any other internal or list type that is defined in either the same ultra file or in another. See the example below.

Example - Internal formats

Case 1:

internal I1 {
I2 f1;
};
internal I2 {
list<int> f1;
};

Case 2:

49
In file A
internal I1 {
<foldername>.<filename>.I2 f1;
//When referring an internal from another file
that is in the same folder, the folder name can be omitted.
};
In file B
internal I2 {
list<int> f1;
};

List types are declared as follows:

list< ElementType >

where ElementType may be any of the previous, including an internal format identifier, or another list type.

Example - List type

internal I1 {
list<list<I2> > f1;
};

It is also possible to specify a field as optional:

Example - Specifying a field as optional

internal I1 {
drudr f1: optional;
};

Similarly, you declare a map field type this way:

map< ElementType, ElementType >

Example - Declaring a map field

internal I1 {
map<string, int> f1;
};

Internal formats can also be automatically generated from in_map definitions. For further information, see target_internal specification in
11. In-maps.

Class Specifications
All internal formats will be compiled into Java classes. It is possible to specify additional interfaces for the class to implement:

Example - Class specifications

50
internal I1 :
implements("Interface1"), implements("Interface2") {
...
};

However, this requires that Interface1 and Interface2 only declare methods that are later generated by Ultra when it creates the
Java class. For further information about methods and types for UDR type methods, see the Development Toolkit user's guide.

Format Inheritance
It is possible to use alternative base UDR definitions for the generated Ultra classes by use of the extends_class or extends option.

extends_class is used by some MediationZone agents (for instance, the HTTP agent) for better processing support.

Example - extends_class

internal I1 :
extends_class( com.mysite.myDTKUltraFormat ) {
...
};

extends lets a format inherit fields defined in an ancestor.

Example - extends

internal A {
int a;
...
};
internal B : extends ( A ) {
int b;
...
};

Multiple inheritance is not supported, i e you can only use the extends or extends_class option once in the definition of an internal
format.

Event Types
It is possible to declare user defined event types in Ultra by using the event keyword instead of internal. Such an event is a special
type of internal format with added event processing support.

51
11. In-maps
The in_map construct is used to map external formats to internal formats during decoding. The general syntax for an in-map declaration
is as follows:

in_map <map_name> : <in_map options> {


<explicit map specifications>
<automatic> : <automatic options> {
<automatic mapping specifications>
};
<sub-external specifications>
};

The general in-map options are:

Option Description

external Specifies the external format to map from. This is a mandatory parameter.
(<external_name>)

internal The internal format name to map to. This is a mandatory parameter, unless target_internal is
(<internal_name>) specified.

target_internal The target internal format name created when automatically generating a map. This parameter is
(<target_internal_name>) only valid for automatic mappings.

discard_output Specifies that the in_map will produce no output when used in a decoder. This can be useful for
uninteresting "filler records" that are not needed for processing.

emit_field(field1, Specifies that a decoder using this in_map will not route out the top level UDR. Instead, the content
field2, ...) (records or list of records) of the named fields are routed.

Specially named options can also be supplied in the in_map depending on the type of the external format. The type specific options are:

Option Description

ipdr_compact Only applicable for XML based formats (IPDR formats). Specifies that the IPDR "compact" encoding is to be used.

PER_aligned Only applicable for ASN.1 based formats. Specifies that PER encoding (ALIGNED version) is to be used.

PER_unaligned Only applicable for ASN.1 based formats. Specifies that PER encoding (UNALIGNED version) is to be used.

Option Description

<explicit map Describes how the external fields are mapped to the internal fields. These specifications are optional.
specifications>
i:<internal field> and
e:<external field>
[ using in_map <sub_map> ] ;

<automatic> Specifies that all external fields not explicitly mapped in <explicit_map_specifications> will be
implicitly mapped according to the external formats implicit type conversions. It is also possible to control the
behavior of the automatic mapping through options or a specification block, see the section below,
Automatic Maps, for details).

<sub-external This is used to handle the special case where the mapped external is the parent of other externals that must
specifications> be considered for decoding. This is currently only supported for XML schema based externals where it is
used to support, for instance, IPDR decoding.

ignore_unknown_tags This option can be used to ignore unknown tags when the external format is ASN.1 BER.

52
Example - ignore_unknown_tags

Adding the following for an in_map:

in_map inMapIgnoringUnknownTags:
external(Udr),
target_internal(Udr),
ignore_unknown_tags {

automatic;

};

will create an in_map called inMapIgnoringUnknownTags that will simply ignore any unknown
tags in the BER encoding.

Note!

This functionality can also be achieved by using the ASN.1 extensibility syntax. For
example, entering:

TheType ::= SEQUENCE {

boolField (1) BOOLEAN OPTIONAL,

will result in tags of the type TheType to be ignored.

11.1 Examples of Non-Automatic Maps


Non-automatic maps provide explicit mapping between the external and internal format. Such mappings work on fields with primitive types
and/or fields that are instances of other formats. In the latter case, the maps can be nested indefinitely. The following two examples
demonstrate these concepts.

Example - Non-Automatic Maps, all primitives

In the following example a decoder, based on the in_map, will do the following:

1. Read an instance of the external record ExtFormat.

2. Associate ef1 with if1, that is, any reference to if1 will force decoding of the information of ef1 and in the process
convert it to an integer.

3. Associate ef2 with if2, that is, any reference to if2 will force decoding of the information of ef2 and in the process
convert it to a string.

external ExtFormat {
ascii ef1 : static_size(4), padded_with(" ");
int ef2 : static_size(2);
};

internal IntFormat {
int if1;
ascii if2;
};

53
in_map InMapFormat: external(ExtFormat), internal(IntFormat) {
e:ef1 and i:if1;
e:ef2 and i:if2;
};

Example - Non-Automatic Maps, externals in externals

In the following example a decoder, based on the in_map, will do the following:

1. Read an instance of the external record ExtFormat2.

2. Associate ef1 with if1, that is, any reference to if1 will force decoding of the information of ef1. The conversion
between ExtFormat1 andIntFormat1 is, in this case, dictated by the in-map named InMapFormat1 as declared in
InMapFormat2.

external ExtFormat1 {
ascii ef1 : static_size(4);
};

external ExtFormat2 {
ExtFormat1 ef1;
};

internal IntFormat1 {
int if1;
};

internal IntFormat2 {
IntFormat1 if1;
};

in_map InMapFormat1: external(ExtFormat1), internal(IntFormat1) {


e:ef1 and i:if1;
};

in_map InMapFormat2: external(ExtFormat2), internal(IntFormat2) {


e:ef1 and i:if1 using in_map InMapFormat1;
};

As seen in the example, explicit map specifications for complex formats can become very large, even describing all the internals to map
data too, can be a huge task. This is why automatic mapping is normally used.

11.2 Automatic Maps


Automatic mapping is used when all the fields of the external format will be included in the internal format. This does not preclude explicit
mapping of selected fields or types. Automatic mapping complies with the following steps for each field in the external format:

1. Evaluate if the in_map contains an explicit mapping for the external field. If so, use that explicit mapping.

2. If the preceding step fails, evaluate if the internal format (if specified) contains a field name that is identical to the external field
name. If so, create with appropriate type conversions, a mapping between the two field names.

3. If the preceding step fails and the external field is not marked as external_only, create a new field (with the same field name
and the appropriate type) in the target_internal. Then create a mapping from the external field to this new internal field.

54
The type of the created internal field is controlled by the external field type.

External Ultra Type mapped to


Type

Primitive Maps to the corresponding primitive internal type. This depends on the external format specification.
external

External Unless a using in_map specification is applicable, a new in_map is automatically created to handle the mapping
format between the external sub-record and the internal field. The automatic map has by default no specified internal or
(sub- target_internal name, however this can be modified with automatic mapping specifications.
record)

List Maps to an internal list type, where the list element type is deduced from the external list element type according to these
rules (recursively).

target_internal
When using automatic, a new internal format may be automatically generated if any new internal fields are needed for the automatic
mapping. The name of this new internal format is given by the target_internal specification of an in_map declaration. If no
target_internal name specification is given, the format will still be generated if needed, however without usable name (that is, the
format cannot be directly referred to in APL).

If a target_internal name has been given, this format will be created even if no new fields are created.

If the in_map has a specified internal format, the generated format is a subtype of this format (hence, inherits all its fields).

Automatic Mapping Specifications


It is possible to modify the behavior of the automatic mapping algorithm when generating the sub-maps. The most direct way to do this is to
specify how each external type will be mapped, either by explicitly specifying the sub-map or specifying the internal and/or
target_internal name.

The syntax for the two forms of mapping specifications is declared as follows:

<external type>: <in_map (target_)internal options> ;

and

<external type>: using in_map <map name>;

Example - Automatic mapping specifications

internal IntBase {
int recordSequenceNumber;
};
asn_block {
ExtDataFile ::= CHOICE(
mc MobileCall,
dp DataPacket)
MobileCall ::= SET ( ... )
DataPacket ::= SET ( ... )
};

in_map InMapDataFile :
external(ExtDataFile),
target_internal(IntDataFile) {
automatic;
};

55
In this case two new internal types will automatically be generated for MobileCall and DataPacket. However, they must usually be
given a name or in other cases inherit the fields of some declared base internal format. This can be accomplished by adding some
automatic mapping specifications:

Example - Adding automatic mapping specifications

in_map InMapDataFile :
external(ExtDataFile),
target_internal(IntDataFile) {
automatic {
MobileCall: internal(BaseInt),
target_internal(TIMobileCall);
DataPacket: internal(BaseInt),
target_internal(TIDataPacket);
};
};

This could also be done by specifying a using in_map declaration. For instance, the previous example is equivalent to the more
complicated one:

Example - using in_map declaration

in_map MobileCallMap: internal(BaseInt),


target_internal(TIMobileCall) {
automatic;
};

in_map DataPacketMap: internal(BaseInt),


target_internal(TIDataPacket) {
automatic;
};

in_map InMapDataFile :
external(ExtDataFile),
target_internal(IntDataFile) {
automatic {
MobileCall: using in_map MobileCallMap;
DataPacket: using in_map DataPacketMap;
};
};

The type map specifications are inherited by every automatically generated sub-map:

Example - type map specifications inherited

asn_block {
...
MobileCall ::= SET (location Location;)
...
};
external Location {
int longitude;
int latitude;
};

in_map InMapDataFile :
external(ExtDataFile),
target_internal(IntDataFile) {
automatic {
MobileCall: internal(BaseInt),
target_internal(TIMobileCall);
DataPacket: internal(BaseInt),
target_internal(TIDataPacket);

56
Location: internal(BaseInt),
target_internal(TILocation);
};
};

In this example, even though Location is not a part of the direct mapping of ExtDataFile itself, the type map information will still be
considered when creating TIMobileCall.

use_external_names Automatic Option


When declaring a target_internal name for a large number of sub-formats the use_external_names option on the automatic
block is a useful option. It will give all generated maps a target_internal name identical to the name of the external format.

Example - use_external_names option on the automatic block

in_map AMARecord_Map : external(AMARecord),


target_internal(AMARecord) {
automatic {
RecordOwnerType : target_internal(RecordOwnerType);
RecordOwnerDN : target_internal(RecordOwnerDN);
Package_100 : target_internal(Package_100);
Package_101 : target_internal(Package_101);
Package_102 : target_internal(Package_102);
};
};

The use_external_names specification will ease the syntax by removing the need of all the target_internal specifications. The
general syntax for a use_external_names declaration is as follows:

use_external_names(<external 1>, <external 2>, ...)

The previous example includes all of the specified external definitions within the file. Hence, the in_map could be rewritten as:

Example - in_map

in_map AMARecord_Map : external(AMARecord),


target_internal(AMARecord) {
automatic : use_external_names;
};

It is possible to combine this with automatic mapping specifications. In this case the explicit specifications will override the
use_external_names behavior.

Automatic Block Internal Specification


It is also possible to append internal type specifications to the constructed in_maps:

Example - Internal type specifications

in_map AMARecord_Map : external(AMARecord),


target_internal(AMARecord) {
automatic : use_external_names, internal(myInternal);
};

57
If automatic mapping specifications are in use, these will override the internal specifications on the automatic level (even if they only
specify target_internal).

11.3 discard_output Specification


Sometimes it is necessary to describe parts of the external format as an in_map, despite the lack of interest in processing the decoded
data. That is, when there are more complex types of filler data and this information is to be discarded.

Example - discard_output specification

in_map FillerInMap: external(FillerRecord),


internal(EmptyInternal), discard_output {
automatic;
};

in_map DataInMap: external(DataRecord),


target_internal(DataTarget) {
automatic;
};

decoder TotalDecoder: in_map(FillerInMap), in_map(DataInMap);

This example will produce DataTarget UDRs and discard any filler (assuming that the referred formats have been properly defined).

58
12. Decoders
A decoder specifies how data is arriving from a source. There are two basic types of decoders:

Simple decoders - Decode one or more external formats.


Constructed decoders - Coordinate the decoding between multiple simple or constructed decoders.

Simple Decoders
The syntax of a simple decoder is declared as follows:

decoder <name> : <decoder options>;

The decoder options are:

Option Description

in_map(<map name>) Specifies what in-maps to use. At least one is required.

block_size(<size>) Specifies that this is a blocked format with specified block size.

terminated_by(<terminator>) Specifies the block filler used. This option has no effect if block_size has not been specified.

The decoder may contain one or several in-maps, depending on whether it manages a single or multiple (mixed) record type. For multiple
maps, the corresponding external records except the last one must support identification. The decoder will try each in_map in the
specified order. The first one, for which the identification criteria are met, will be used.

How the record identification is specified depends on the actual external record type. For example, sequential external records must use
the identified_byoption while BER encoded records support identification by the standard tagging scheme.

Simple decoders may be given blocking information through the block_size and terminated_by constructs. The block_size
parameter contains the size of a block and the terminated_by parameter specifies the start of a padding character.

Example - Simple decoder

decoder SimpleDecoder : in_map(Map1), in_map(Map2),


block_size(2048), terminated_by(0x00);

This decoder starts by reading the next byte and evaluates if it equals 0x00. Should it be 0x00, it will jump to the next even boundary of
2048 and repeat this procedure. If it is not 0x00, it will evaluate the identification for both of the externals specified in Map1 and Map2 (in
that order).

Constructed Decoders
A constructed decoder is defined in terms of a number of other simple or constructed decoders. They are used to specify the decoders to
be used in sequence, for example to specify separate decoders managing header, trailer, and record information. For instance, three
external record types (Rheader, Rudr, Rtrailer), with corresponding in_maps (Mheader, Mudr, and Mtrailer) and decoders (
Dheader, Dudr, and Dtrailer).

The following constructed decoder specifies the header record to be decoded first. Then any number of UDR records are decoded,
followed by a single trailer record.

Example - Constructed decoder

59
decoder TTFile {
decoder Dheader;
decoder Dudr *;
decoder Dtrailer;
};

As can be seen, the constructed decoder does not (and cannot) have any decoder options.

The asterisk after Dudr indicates that zero or more entries can occur before a terminating trailer record. For such a sub-decoder, the
constructed decoder will switch to the next decoder when the sub-decoder cannot handle the input (as deduced by the identification criteria
of the in_maps in the sub-decoder). In this case this means that the Dudr decoder must support identification logic, or the Dtrailer
decoder will never be reached (and the decoder will abort with an "Unexpected EOF" error).

This example could also have been supported with a simple decoder:

Example - Simple decoder

decoder TTFile :
in_map(Mheader), in_map(Mudr), in_map(Mtrailer);

The difference is that the order of header, UDR, and trailer would not be enforced for the simple decoder. The simple decoder will also not
work if, for instance, the header does not support identification.

60
13. Out-maps
Out-maps are used in encoders to map an internal format to an external format.

Example - out_map

internal IFormat1 {
int f1;
ascii f2;
};
external EFormat1 {
ascii f1 : static_size(2), terminated_by(" ");
ascii f2 : static_size(10), terminated_by(" ");
};
out_map OutMap : internal(IFormat1), external(EFormat1) {
automatic;
};

The automatic mapping for out-maps only attempts to bind every field name of the external format to the respectively internal format. Just
as with the in-maps, no additional formats or fields are created. Like the in-maps, out-maps support explicit mappings.

As for in-maps, specially named options can also be supplied in the out_map depending on the type of the external format. The type
specific options are:

Option Description

PER_aligned Only applicable for ASN.1 based formats. Specifies that PER encoding (ALIGNED version) is to be used.

PER_unaligned Only applicable for ASN.1 based formats. Specifies that PER encoding (UNALIGNED version) is to be used.

Optional Fields
There are some additional considerations for the case where optional external fields are encoded.

An internal field can be defined as optional, enabling to encode the corresponding external field as not present. Internal fields with value
null may still be considered present and are encoded as, for instance, an empty string. To override this, use the APL command
udrUnsetPresent prior to encoding.

Consider the following example, where an optional field within an incoming ASN.1 record will be encoded (the definition of myInt varies).

Example - Encoding Optional Fields

asn_block {
main_udr ::= SEQUENCE {
fieldA [APPLICATION 5] INTEGER OPTIONAL
};
};
in_map inM : external( main_udr ),
internal( myInt ) {
automatic;
};
out_map outM : internal( myInt ),
external( main_udr ) {
automatic;
};
decoder myDEC : in_map( inM );
encoder myENC : out_map( outM );

Case 1 - Optional internal field:

61
If fieldA is defined as optional in the internal format definition, it will be present in the encoded record if it was present in the
internal record, even if the value is null. It will not be present if it was not present in the internal record.

internal myInt {
int fieldA : optional;
};

Case 2 - Mandatory internal field:

If fieldA is defined as mandatory in the internal format definition, it will always be present in the encoded record.

internal myInt {
int fieldA ;
};

Case 3 - No mapped internal field:

internal myInt {
};

62
14. Encoders
An encoder specifies how data is to be encoded. The syntax for the encoders is as follows:

encoder <encoder_name> : <encoder options>;

The encoder options are:

Option Description

out_map(<map name>) Specifies what out-maps to use. At least one is required.

block_size(<size>) Specifies that this is a blocked format with a certain block size

terminated_by Specifies the block filler used. This option has no effect if the block_size has not been
(<terminator>) specified.

When encoding a record, the encoder tries each out-map in the order specified. If the out-map can encode the record, then this out-map is
used, otherwise the next out-map is tried.

An out-map can encode the data if:

1. The record type matches the internal type specified in the out-map.

2. All format specific requirements are met. At the moment this evaluation is performed only for sequential data with an
identified_by condition. Only data where the mapped fields meet the identification rule will be accepted. This is to support
mapping to different external record types from the same internal type.

Note!

There is no such thing as a constructed encoder. If the records are required to be forwarded in a specific order, an APL
agent handling the output logic must precede the Encoder.

63
15. A Constructed Decoder Example
In some cases, a so-called constructed decoder is useful. The main advantage is that it introduces some validation logic, making it
possible to evaluate the order of the arriving records. For instance, suppose the incoming files contain one header and one trailer which
must be present at file start and end, in order for the file to be accepted. In between, data records may or may not be present. The data
records can be of two types. Headers and trailers are considered being records as well, so there will actually be four record types in this
format definition.

The source file for which a decoder is exemplified in this appendix:

An example of the source files discussed in this section

This section includes a detailed description of all the code block parts that a constructed decoder might contain:

Headers and Trailers (which are included in the external code block)
external
internal
in_map
decoder
out_map
encoder

external
Both headers and trailers as well as record types should be included in the external code block.

Headers and Trailers


Since headers and trailers are treated as records, the same syntax as for external records apply. APL syntax can also be used within
UFDL code.

external FileHeader {
ascii header : terminated_by(0xA);
};

The first line is always a header.

Note!

No identified_by is needed, since the decoder will not evaluate the input stream to see if the next record is a leader or not.
This compared to whether to read a TypeA or TypeB record, when an identification test is called for.

64
external FileTrailer : identified_by( strStartsWith( trailer, "Date") ){
ascii trailer : terminated_by(0xA);
};

The identified_by for the trailer is not crucial, however, it provides additional validation during decoding, since it evaluates that the
trailer really starts with "Date".

Record Types
The definitions of TypeA and TypeB are fairly straight forward. No encode_value for RecordType is set, since this is evaluated from the
internal UDR during encoding (see the section below, internal).

external TypeA : identified_by( RecordType == "A" ),


terminated_by(0xA) {
ascii RecordType : static_size(2),
terminated_by(":");
ascii A_number : terminated_by(":");
ascii B_number : terminated_by(":");
ascii SequenceNumber : terminated_by(":");
ascii Duration : terminated_by(0xA);
};

external TypeB : identified_by( RecordType == "B" ),


terminated_by(0xA) {
ascii RecordType : static_size(2),
terminated_by(",");
ascii CallingCountry : terminated_by(",");
ascii SequenceNumber : terminated_by(",");
ascii LocalAreaCode : terminated_by(",");
ascii A_number : terminated_by(",");
ascii B_number : terminated_by(",");
ascii CauseForOutput : terminated_by(",");
ascii CalledCountry : terminated_by(0xA);
};

internal
Suppose it is desired to output one record type as a replacement for the incoming types A and B the simplest way is to create a mutual
internal.

internal MyInternal {

// These are common fields to TypeA and TypeB

string RecordType;
string SequenceNumber;
string A_number;
string B_number;

// These may or may not be present depending on


// record type

string CallingCountry: optional;


string LocalAreaCode: optional;
string Duration: optional;
string CauseForOutput: optional;
string CalledCountry: optional;
};

Both TypeA and TypeB records are mapped to MyInternal. The common fields will always be set. The others are defined as optional,
hence, their presence depends on the record type. The RecordType in the internal type is required for encoding, since the encoder needs
to evaluate the record type to decide whether to encode as TypeA or TypeB.

65
in_map
Both record types A and B are mapped to the same internal. This approach is useful to simplify APL syntax within processing (a lot of if-
statements used to determine the record type, can be eliminated), or in case one resulting output type is produced.

TypeA and TypeB are both mapped to MyInternal (see the section above, internal).

in_map TypeA_in : external( TypeA ), internal( MyInternal ) {


automatic;
};

in_map TypeB_in : external( TypeB ), internal( MyInternal ) {


automatic;
};

The headers are not wanted in processing, therefore discard_output is set. However, the target_internal is still useful since it
enables you to produce headers for encoding.

in_map Header_in : external( FileHeader ),


target_internal( Header ), discard_output {
automatic;
};

// The trailer gets a special record type.

in_map Trailer_in : external( FileTrailer ),


target_internal( Trailer ) {
automatic;
};

decoder
The following constructed decoder definition will expect all batches to start with a header, end with a trailer and have zero/one/several A
and B records in between. If not, the decoder will abort.

// The sub-decoders.

decoder Records : in_map( TypeA_in ), in_map( TypeB_in );


decoder Header : in_map( Header_in );
decoder Trailer : in_map( Trailer_in );

// The total (file) decoder.


// '*' means zero/one/several records are expected between
// one header and one trailer for each file collected.

decoder Total {
decoder Header;
decoder Records *;
decoder Trailer;
};

out_map
Suppose you are required to encode back to the original format.

out_map TypeA_out: external(TypeA), internal( MyInternal ) { automatic;


};

66
out_map TypeB_out: external(TypeB), internal( MyInternal ) {
automatic;
};

out_map Trailer_out: external(FileTrailer), internal(Trailer) {


automatic;
};

out_map Header_out: external(FileHeader), internal(Header) {


automatic;
};

The out-maps and encoder are simple.

Note!

TypeA and TypeB both are encoded from MyInternal. Which type to use depends on the value of the RecordType field.

encoder
A constructed encoder cannot be created. Hence, the following encoder definition will not care for the order of arriving records, nor that all
types must be present in the output file.

encoder Total: out_map( TypeA_out ),


out_map( TypeB_out ),
out_map( Header_out ),
out_map( Trailer_out );

67
16. A Sequential Format Example
The example illustrated in this appendix is a format definition for decoding a shortened version of EWSD AMA (Automatic Message
Accounting).

All incoming records are of the same record type, however their content varies. The first four fields are present in all records, the fifth field
varies depending on the content of the field RecordOwnerTypePresent. The last three fields are optional.

A schematic representation of the example discussed in this section

external
Since the last three optional fields (including RecordOwnerType and RecordOwnerDN) consist of several fields, each one will be defined
as its own type. That is, an external sequential format.

Note!

The FillerRecord_0x00_EXT construct. This is the padding which may be present between records. Hence, it is defined as a
record type identified by the decoder, however not routed on to the subsequent agent (see the in_map definitions).

If the format is only used for decoding (which is the normal case for switch output formats), the encoding instructions (encode_value) in
the following code is skipped.

external AMARecord_EXT:
identified_by(RecordIdentifier == 0x84),
dynamic_size(RecordLength)

{
int(little_endian) RecordIdentifier :static_size(1),
external_only, encode_value(0x84);
int(little_endian) RecordLength :static_size(2),
external_only, encode_value(udr_size);

This byte contains several flags, from which only one is of interest. Therefore, if RecordOwnerTypePresent is set, then the
RecordOwnerType is present, or else the RecordOwnerDN data is. See the presence specifications in the following code.

bit_block :static_size(1) {
int(little_endian) RecordOwnerTypePresent: msb(7),lsb(7),
external_only, encode_value(
(field_present(RecordOwnerType)?1:0));
};

Since three bytes of unwanted data is present, it is specified as external_only to stop the field from getting automatically generated in
the target internal. No encoding is specified (0 padding is used).

byte ignoredFields: static_size(3), external_only;

68
Either RecordOwnerType or RecordOwnerDN is present. To encode them, it is important to note that exactly one of these fields must be
present in the output data.

int(little_endian) RecordOwnerType: static_size(1),


present if(RecordOwnerTypePresent == 1);
RecordOwnerDN_EXT RecordOwnerDN:
present if(RecordOwnerTypePresent == 0);

The rest of the record consists of optional packages with additional information. The full AMA format contains lots of other packages (and
additional information in the header), however in this example, only three packages are included. Any unrecognized package leads to
failure of the decoding, since the size of the set is specified (all the remaining data must be handled by the set decoding).

set: dynamic_size(remaining_size) {
Package_100_EXT DateTimeDuration : optional;
Package_101_EXT PartnerDirectoryNumber : optional;
Package_102_EXT ServiceInfo : optional;
};
};

external RecordOwnerDN_EXT
{
bit_block : static_size(1) {
int LACLength : msb(7), lsb(4);
int OwnerIDLength : msb(3), lsb(0),
external_only, encode_value( strLength( LACAndDN ));
};

The following is a typical construction for BCD data with a nibble length specification. Both nibble size (native_size) and field size (
dynamic_size) must be specified.

bcd LACAndDN : dynamic_size((OwnerIDLength+1)>>1),

// Alternative syntax:
// dynamic_size((OwnerIDLength+1)/2)

native_size(OwnerIDLength);
};

//Package with PackageNumber 100 (0x64):


external Package_100_EXT: identified_by(PackageNumber == 0x64)
{
int(little_endian) PackageNumber : static_size(1),
external_only, encode_value( 0x64 );
int(little_endian) Year : static_size(1);
int(little_endian) Month : static_size(1);
int(little_endian) Day : static_size(1);
int(little_endian) Hour : static_size(1);
int(little_endian) Minutes : static_size(1);
int(little_endian) Seconds : static_size(1);
int(little_endian) Flags : static_size(1);
int(little_endian) Duration : static_size(3);
};
// Package with PackageNumber 101 (0x65).
external Package_101_EXT: identified_by(PackageNumber == 0x65)
{
int(little_endian) PackageNumber: static_size(1),
external_only, encode_value( 0x65 );
int(little_endian) NumberOfDigits: static_size(1),
external_only, encode_value( strLength( Digits ));

// Again the typical BCD decoding specification

bcd Digits: dynamic_size((NumberOfDigits+1)/2),


native_size(NumberOfDigits);
};

// Package with PackageNumber 102 (0x66).


external Package_102_EXT: identified_by(PackageNumber == 0x66)
{
int(little_endian) PackageNumber : static_size(1),

69
external_only, encode_value( 0x66 );
int(little_endian) ServiceIndicator : static_size(1);
int(little_endian) AdditionalInformation : static_size(1);
int(little_endian) Flags : static_size(1);
};

Note!

The identified_by expression, which must be specified for any format used in a set construct.

/ Filler needed to be able to recognize on the input stream:


external FillerRecord_0x00_EXT:
identified_by(RecordIdentifier == 0x00), static_size(32)
{
int(little_endian) RecordIdentifier : static_size(1),
external_only, encode_value(0x00);

// Note that no other fields are specified.


// The UDR size (32) will be consumed and discarded (see in_map).

};

Alternative Syntax
An alternative to use the set construct, in the external definition for AMARecord_ext, switched_set could be used. This will impact the
syntax for the Package_*_EXT types. Only the syntax differing from the original example is shown. The main reason for using
switched_set instead of set is when performance must be increased.

external AMARecord_EXT:

// ...
// All preceding fields according to the original specification.
// Only the set construct is replaced with switched_set.
// ...

switched_set(PackageNumber): dynamic_size(remaining_size) {
case( 0x64 ) {
Package_100_EXT DateTimeDuration;
};
case( 0x65 ) {
int(little_endian) NumberOfDigits_101: static_size(1),
external_only, encode_value( strLength( Digits_101 ));

// Again the typical BCD decoding specification.

bcd Digits_101: dynamic_size((NumberOfDigits_101+1)/2),


native_size(NumberOfDigits_101);
};
case( 0x66 ) {
int(little_endian) ServiceIndicator_102 : static_size(1);
int(little_endian) AdditionalInformation_102 : static_size(1);
int(little_endian) Flags_102 : static_size(1);
};
};
};

external RecordOwnerDN_EXT
{
bit_block : static_size(1) {
int LACLength : msb(7), lsb(4);
int OwnerIDLength : msb(3), lsb(0),
external_only, encode_value( strLength( LACAndDN ));
};

The following is a typical construction for BCD data with a nibble length specification. Both nibble size (native_size) and field size (
dynamic_size) must be specified.

70
bcd LACAndDN : dynamic_size((OwnerIDLength+1)>>1),
native_size(OwnerIDLength);
};

// Package with PackageNumber 100 (0x64).


external Package_100_EXT
{
int(little_endian) Year : static_size(1);
int(little_endian) Month : static_size(1);
int(little_endian) Day : static_size(1);
int(little_endian) Hour : static_size(1);
int(little_endian) Minutes : static_size(1);
int(little_endian) Seconds : static_size(1);
int(little_endian) Flags : static_size(1);
int(little_endian) Duration : static_size(3);
};

Note!

No identified_by or PackageNumber is present, since this is handled in the containing switched_set .

internal
No internal is used. In this case, the target_internal is sufficient; all field names and field types are in order and there is only
one type of record present in the input, and no additional fields are required.

in_map
The padding in the records is recognized by the decoder, however it is not actually mapped in to the system due to the use of the
discard_output flag.

The AMARecord_Map contains sub-automatic specifications (the target_internal specifications within the automatic block), which
will give five additional internal formats (other than the AMARecord). This is useful when you want to route them as individual records.

in_map FillerRecord_0x00_Map :
external(FillerRecord_0x00_EXT),
target_internal(FillerRecord_0x00),
discard_output {
automatic;
};
in_map AMARecord_Map : external(AMARecord_EXT),
target_internal( AMARecord ) {
automatic {
RecordOwnerType_EXT : target_internal( RecordOwnerType );
RecordOwnerDN_EXT : target_internal( RecordOwnerDN );
Package_100_EXT : target_internal( Package_100 );
Package_101_EXT : target_internal( Package_101 );
Package_102_EXT : target_internal( Package_102 );
};
};

decoder
The padding pseudo-records and data records can arrive in any order, therefore there is no need to define a constructed decoder.

decoder AMAFile : in_map( AMARecord_Map ),


in_map( FillerRecord_0x00_Map );

71
17. An ASN.1 Format Example
This appendix shows how incoming ASN.1 records can be encoded into a sequential external format.

An ASN.1 format definition can be pasted directly into an Ultra asn_block. Nested structures will be saved as they are in the
target_internal type, however the nested fields cannot easily be encoded to a different format without using an APL agent (Analysis
or Aggregation). In the example, the incoming ASN.1 record is encoded to a sequential record, mapping all the field values to
corresponding fields in the sequential format.

Mapping - nested structures to plain, sequential structures - can be accomplished in three ways:

By creating a constructed external format definition, that is, where the external record definition consists of sub-externals.
By extending the target_internal with first level temporary fields which will hold the values of the nested fields to be
encoded.
By creating a new UDR which is populated with values from the incoming nested UDR.

Constructed Internal
It is possible to encode to a constructed sequential external, that is, an external sequential definition containing other externals to represent
the nested fields. The disadvantage with this approach is that it is not possible to mix different level fields in the produced output record.

external
Name the fields in the outgoing external exactly as in the incoming ASN.1 structure. This allows the use of automatic mapping.

asn_block {

exchangeRec DEFINITIONS IMPLICIT TAGS ::=


BEGIN

main_udr ::= SEQUENCE


{
duration [ APPLICATION 1 ] INTEGER OPTIONAL,
calledNumber [ APPLICATION 2 ] INTEGER OPTIONAL,
callingNumber subUDR1
}

subUDR1 ::= [ APPLICATION 3 ] SEQUENCE


{
category [ APPLICATION 4 ] INTEGER OPTIONAL,
adressString subUDR2
}

subUDR2 ::= [ APPLICATION 5 ] SEQUENCE


{
number [ APPLICATION 6 ] INTEGER OPTIONAL,
ton [ APPLICATION 7 ] INTEGER OPTIONAL,
npi [ APPLICATION 8 ] INTEGER OPTIONAL
}

END
};

//-------------------------------------------------------

external out {
ascii duration : static_size(2);
ascii calledNumber : static_size(8);
subUDR1 callingNumber : static_size(8);
};

external subUDR1 {
ascii category : static_size(2);
subUDR2 adressString : static_size(6);
};

external subUDR2 {

72
ascii number : static_size(2);
ascii ton : static_size(2);
ascii npi : static_size(2);
};

in-map and out_map


Automatic mapping considers sub-UDRs as well.

in_map inM : external( main_udr ),


target_internal( myTI ) {
automatic;
};

out_map outM : internal( myTI ),


external( out ) {
automatic;
};

decoder and encoder


decoder myDec : in_map( inM );

encoder myEnc : out_map( outM );

Extending the target_internal


Create an internal which will hold the nested fields to be mapped to the sequential format. Define a target_internal holding both
the asn_block and the internal. The values of the nested fields of the target_internal are copied to the fields added with the
internal format specification, using APL code.

APL code is necessary to extract the nested values

Format Definition (shortened):

asn_block {
:
main_udr
:
};

73
Note!

All following fields are declared optional. This is to enable differentiation between an absent value and a zero value (default for
int type). Thus, if no value is entered it will be encoded as empty in output format, as opposed to 0 (zero).

internal exchangeRec_Int {
int duration_i : optional;
string calledNumber_i : optional;
int category_i : optional;
string number_i : optional;
string ton_i : optional;
string npi_i : optional;
};

Note!

Since no automatic mapping specifications are given, no named internal types for SubUDR1 and SubUDR2 are received. This is
not a problem as long as referencing the types directly (for instance in APL) is unnecessary.

in_map exchangeRecord_MAP_IN:
external(MainUdr),
internal(exchangeRec_Int),
target_internal(exchangeRec_TI){
e:duration and i:duration_i;
e:calledNumber and i:calledNumber_i;
automatic;
};

A structure of sub-UDRs with the same field names as in the internal mapped from is created (that is, the target_internal which has
the same structure as the ASN.1 external). This will only produce a line-based comma separated output file.

external ConstructedOut: terminated_by("\n") {


ascii duration : terminated_by(","), int(base10);
ascii calledNumber : terminated_by(",");
SubOut1 callingNumber;
};

external SubOut1: terminated_by("\n") {


ascii category : terminated_by(","), int(base10);
SubOut2 adressString;
};

external SubOut2: terminated_by("\n") {


ascii number : terminated_by(",");
ascii ton : terminated_by(",");
ascii npi : terminated_by("\n");
};

out_map Constructed_Map:
external(ConstructedOut),
internal(exchangeRec_TI) {
e:duration and i:duration_i;
e:calledNumber and i:calledNumber_i;
automatic;
};

encoder ConstructedEnc: out_map(Constructed_Map);

APL Code:

74
consume {

if ( udrIsPresent( input.callingNumber.adressString ) ) {
input.category_i = input.callingNumber.category;
input.number_i = input.callingNumber.adressString.number;
input.ton_i = input.callingNumber.adressString.ton;
input.npi_i = input.callingNumber.adressString.npi;
}

udrRoute( input );
}

Creating a New UDR


Create an internal for the sequential format, however do not add it to the incoming ASN.1 structure's target_internal. Instead for
each incoming UDR, create a new UDR and copy the field values from the ASN.1 UDR. The ASN.1 UDR can then be discarded, routing
the new internal further.

Internal presentation of the input ASN.1 UDR

Internal presentation of the output sequential UDR

APL Code Definition:

exRec.exchangeRec_Int outUDR = udrCreate( exRec.exchangeRec_Int );

consume {
outUDR.duration_i = input.duration;
outUDR.calledNumber_i = input.calledNumber;

if ( udrIsPresent( input.callingNumber.adressString ) ) {
outUDR.category_i = input.callingNumber.category;
outUDR.number_i = input.callingNumber.adressString.number;
outUDR.ton_i = input.callingNumber.adressString.ton;
outUDR.npi_i = input.callingNumber.adressString.npi;

75
}

udrRoute( outUDR );
}

external
The ASN.1 definition can be copied directly into an Ultra asn_block definition. An external for the sequential outgoing UDRs is created.

Format Definition (shortened):

asn_block {
:
main_udr
:
};

// The same field names as in the internal format are used


// to be able to use automatic mapping.
external exchangeRecSEQ
ascii duration_i
ascii calledNumber_i
ascii category_i
ascii number_i
ascii ton_i
ascii npi_i
};

internal
An internal, containing fields matching the external field names, is created in order to hold the values to be encoded.

internal exchangeRec_Int {
int duration_i : optional;
string calledNumber_i : optional;
int category_i : optional;
string number_i : optional;
string ton_i : optional;
string npi_i : optional;
};

in_map and out_map


The incoming external is turned into a target_internal , without adding any fields. The internal is mapped to the sequential
external format.

in_map exchangeRecord_MAP_IN_II:
external(main_udr),
target_internal(exRec_TI){
automatic;
};

out_map exchangeRecord_MAP_OUT:
external(exchangeRecSEQ),
internal(exchangeRec_Int) {
automatic;
};

76
decoder and encoder
decoder exchangeRec: in_map(exchangeRecord_MAP_IN);

encoder exchangeRecSEQ: out_map(exchangeRecord_MAP_OUT);

77
18. XML Schema Support
This chapter describes the XML addition to the Ultra Format Definition Language (UFDL). This addition enables you to compile a subset of
XML Schema element definitions, and to decode the XML input data.

18.1 Overview
MediationZone you manage XML parsing in UFDL by applying the xml_schema construct.

xml_schema [: <xml_schema option>] { <XML Schema elements> };

The XML Schema Options


The following XML schema options are available:

output_encoding - Use this option to specify the type of encoding used. See the official Java SE Documentation from Oracle for
information regarding supported encodings.

xml_schema : output_encoding("UTF-8") { // ... }

schemaLocation - Use this option to specify the location of a schema that contains qualified schema constructs.

xml_schema : schemaLocation("http://example.com mydocument.xsd") { // ... }

Note!

Only one XML schema option can be used.

The XML Schema Elements


The XML Schema language supports the following element types:

simpleType
simpleType with restrictions
simpleType with union
complexType
complexType with sequence declarations
complexType with extension declarations
complexType with all declarations
Global element declarations and references
SimpleContent elements with extension declarations

Note!

If you want to use union type, you must set the property mz.ultra.xml.restrictions according to your requirements. If
you want to use unions and restrictions inside of unions, set this property to union. If you want to use restrictions everywhere,
including inside the union type, set this property to on. For further information, see 2.6.4 Platform Properties.

78
18.2 External - XML Records
XML Schema complex types are interpreted as UDR types. Elements and attributes within complex types are interpreted as UDR fields,
and field types vary according to the XML schema definitions.

18.2.1 XML Data Type Mapping


XML data types are automatically mapped to data types in Ultra as follows:

XML Type Ultra Type

<integer types> int, except for long and unsignedLong. These data types are mapped to bigint.

float float

double double

dateTime date

anyType The AnyType UDR. See 18.2.2 AnyType UDR Type.

<all other types> string

The mapping of the data types also depends on the element attribute maxOccurs. When the value of this attribute is greater than 1 (one)
or unbounded, the mapped field is of list type.

When the element attribute minOccurs is 0 (zero), the field is considered optional and can be omitted in the internal format.

Declared attributes are considered mandatory or optional, depending on the use attribute:

required - The field is mandatory.


optional (default) - The field is optional.
prohibited - The use attribute is ignored and the field is optional.

Example - Mapping of element- and attribute types

element name="REQUEST">
<complexType>
<!-- Mapped to string, mandatory -->
<attribute name="REQUEST_ID" type="string" use="required"/>
<sequence>
<!-- Mapped to string, mandatory -->
<element name="ITEM" type="string" minOccurs="1" maxOccurs="1"/>

<!-- Mapped to list<string>, mandatory -->


<element name="A_ITEMS" type="string" minOccurs="1" maxOccurs="2"/>

<!-- Mapped to list<string>, mandatory -->


<element name="B_ITEMS" type="string" minOccurs="1" maxOccurs="unbounded"/>

<!--Mapped to string, optional->


<element name="C_ITEMS" type="string" minOccurs="0" maxOccurs="1"/>
</sequence>
</complexType>
</element>

79
18.2.2 AnyType UDR Type
The anyType data type specification in XML Schema is mapped to the AnyType UDR data type in the UltraXML namespace. This UDR
type contains information about all the elements and text contents of the mapped XML element.

The AnyType UDR Type

Field Type Description

attributeMap(map<string, A mapping, from an attribute name to a value, of all the attributes of an element.
string>)

content(string) The text content in the element.

elementMap(map<string, All the sub-elements within the "anyType" element. This is a mapping from the element name to a
list<AnyType(UltraXML)>>) list of all the occurrences. Each sub-element is also an "AnyType" UDR.

Note!

In APL, spaces between the closing angle brackets are required, or compilation will fail.

18.2.3 XML Schema Extensions


You can use the following XML Schema extension attributes in your Ultra code:

contentFieldName
ultraFieldName

contentFieldName Attribute
In an element that is defined to contain text, the text part is normally defined by the name content in the result Ultra type. Another Ultra
field name for this data can be specified by specifying the contentFieldName attribute in the containing element.

ultraFieldName Attribute

For any attribute or element specification that maps to an Ultra field, the Ultra field name is identical to the XML name (possible
namespace is removed). To create a different field name, specify the explicit Ultra field name by using the ultraFieldName attribute.

80
18.3 IPDR Compliance
MediationZone is fully IPDR compliant. UFDL also supports IPDR compact decoding and encoding. To apply decoding or encoding
specify the option ipdr_compact in the map definition.

18.4 XML Schema Limitations


XML Schema Constructs not Supported
The following XML schema constructs are not supported in UFDL:

list type
complexType defined as an extension of a simpleType
group
nillable declarations
include
redefine
substitutionGroup

Validation against XML Schema Definition


XML input data is not fully validated against the XML Schema Definition, so you must ensure that your data is correct. For example, if
maxOccurs is set to 1 you must ensure that that element does not occur more than once.

Support Limitations for Union and Restrictions


Range restrictions have been added to the following predefined types: byte, short, unsignedByte, unsignedShort,
nonPositiveInteger, nonNegativeInteger, positiveInteger and negativeInteger. Range restrictions for the types
that exceed the integer range, such as long, are not supported when using union type and restrictions.
Only string and integer types can be used as base in restrictions, e.g integer, byte, short, positiveInt.
A union can only be referred to via a ref to an element, not via a type.
The following restrictions are not supported: fractionDigits, totalDigits and whiteSpace.

18.5 An XML Format Example


The example in this section describes how to decode and encode XML data.

The example includes the following:

Workflow and Ultra configurations (ultra_xml_example.zip):


CSV_TO_XML_WF - Collects a CSV formatted file and converts it to XML. Optional fields that are not included in the CSV
are populated in the workflow.
XML_TO_CSV_WF - Collects am XML formatted file and converts it to CSV. Optional fields in the XML are discarded.
ULTRA_CSV - CSV decoder and encoder.
ULTRA_XML - XML decoder and encoder.
Input data in CSV format (INFILE01.txt).

Follow these steps to run the example:

1. Download the example files to /opt/ultraXML.


2. Create the subdirectory indata in /opt/ultraXML.
3. Copy INFILE01.txt to /opt/ultraXML/indata.
4. Open the System Importer and select /opt/ultraXML/ultra_xml_example.zip. Import all configurations.
5. Start the workflow CSV_TO_XML_WF. This will create an XML file based on the CSV input in the directory /opt/ultraXML/out.
6. Copy the generated XML file to the directory /opt/UltraXML/indata.
7. Start the workflow XML_TO_CSV_WF. A CSV file that is identical to the downloaded input file will be created in /opt/ultraXML
/out.

81
Below is a description of the Ultra configuration that is used for XML encoding and decoding.

Example - ULTRA_XML

To decode or encode XML data, a format definition (an XML Schema syntax) is included in Ultra xml_schema block.

xml_schema {
<?xml version="1.0" encoding="ISO-8859-1"?>
<schema xmlns = " http://www.w3.org/2001/XMLSchema ">
<element name="TRANSACTION_LOG">
<complexType>
<sequence>
<element ref="TRANSACTION" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
<element name="TRANSACTION">
<complexType>
<attribute name="TXID" type="string" use="required"/>
<sequence>
<element name="USER" type="string" minOccurs="1" maxOccurs="1"/>
<element name="IP" type="string" minOccurs="1" maxOccurs="1"/>
<element name="ITEM" type="string" minOccurs="1" maxOccurs="1"/>
<element name="VALUE" type="long" minOccurs="1" maxOccurs="1"/>
<element name="TIMESTAMP" type="dateTime" minOccurs="1" maxOccurs="1"/>
<element name="CURRENCY" type="string" minOccurs="1" maxOccurs="1"/>
<element name="MISC" type="string" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
</schema>
};

Collected XML UDRs are often terminated by one or several whitespace characters. When mapping, the whitespace temporary
record is identified. Although input data that includes trailing whitespace characters is valid in XML, it is recommended that you
eliminate them when decoding the data. To remove any excessive white spaces from the XML UDRs, the external format
WhiteSpace is used with the in_map of the deocder. For further information see 11. In-maps and 12. Decoders.

external WhiteSpace : identified_by( value == 0x20 || value == 0xA || value == 0xD ) {

int value: static_size(1);


};

The internal and external formats can be mapped automatically but are mapped explicitly in the example. This is to demonstrate
how the interpretation of XML types works.

internal TransactionLog {
list<Transaction> Transactions;
};

internal Transaction {
string TxId;
string User;
string IP;
string Item;
long Value;
date Timestamp;
string Currency;
list<string> Misc;
};

in_map inTransactionLog: external(TRANSACTION_LOG), internal(TransactionLog) {


i:Transactions and e:TRANSACTION using in_map inTransaction;
};

in_map inTransaction: external(TRANSACTION), internal(Transaction) {


i:TxId and e:TXID;
i:User and e:USER;
i:IP and e:IP;
i:Item and e:ITEM;
i:Value and e:VALUE;
i:Timestamp and e:TIMESTAMP;

82
i:Currency and e:CURRENCY;
i:Misc and e:MISC;
};

out_map outTransactionLog: external(TRANSACTION_LOG), internal(TransactionLog) {

i:Transactions and e:TRANSACTION using out_map outTransaction;

};

out_map outTransaction: external(TRANSACTION), internal(Transaction) {


i:TxId and e:TXID;
i:User and e:USER;
i:IP and e:IP;
i:Item and e:ITEM;
i:Value and e:VALUE;
i:Timestamp and e:TIMESTAMP;
i:Currency and e:CURRENCY;
i:Misc and e:MISC;
};

To get rid of white spaces, create a <literal>in_map</literal> for the external format WhiteSpace using the discard_output
option. For further information, see the 11. In-maps.

in_map WS_map: external(WhiteSpace), discard_output {automatic;};

To remove as many white spaces as possible from the processed data, WS_map is set first in the deocder.

encoder encTransactionLog: out_map(outTransactionLog);


decoder decTransactionLog: in_map(WS_map), in_map(inTransactionLog);

83
19. Google Protocol Buffer Support
This chapter describes the GPB (Google Protocol Buffers) addition to the Ultra Format Definition Language (UFDL). This addition enables
you to compile GPB definitions, and to decode the GPB input data as well as encode data into the GPB format.

Both the proto2 and proto3 versions of the google protocol buffers language are supported.

Overview
In MediationZone you manage GPB parsing in UFDL by applying the gpb_block construct. The syntax differs whether you are using
proto2 or proto3.

Syntax for proto2

gpb_block {
<GPB message elements>
};

Syntax for proto3

gpb_block {
syntax = "proto3";
<GPB message elements>
};

The full description of the GPB language for proto2 and proto3 can be found at: https://developers.google.com/protocol-buffers/docs/proto
or https://developers.google.com/protocol-buffers/docs/proto3.

The GPB Field Rules


You specify that message elements are formatted according to one of the following rules:

For proto2 only:

required
optional

For proto2 and proto3:

repeated

The GPB Scalar Value Types


The GPB message elements can be defined with any of the following types:

Type Notes

double 8 bytes signed

float 4 bytes signed

int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values,
use sint32 instead.

int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values,
use sint64 instead.

uint32 Uses variable-length encoding.

uint64 Uses variable-length encoding.

sint32 Uses variable-length encoding. Signed int value. This more efficiently encode negative numbers than regular int32s.

sint64

84
Uses variable-length encoding. Signed int value. This more efficiently encode negative numbers than regular int64s. In
MediationZone sint64 will be more efficient than uint64.

fixed32 Always four bytes. More efficient encoded than uint32 if values are often greater than 228.

fixed64 Always eight bytes. More efficient encoded than uint64 if values are often greater than 256.

sfixed32 Always four bytes.

sfixed64 Always eight bytes.

bool

string

bytes May contain any arbitrary sequence of bytes.

Limitations
The following limitations apply for the GPB support for proto2 in MediationZone:

Default specifiers are not supported.


Groups are not supported.
The packed option is not supported.
Import statements with the gpb_block will have no effect.
Nested types are not fully supported, since their names will become a part of the global scope. However, you can avoid this
problem by changing names on one of the sub types.
The extensions specifier is not supported.
The packed specifier is not supported.
Options are not supported.
Packages are not supported.
Import public specifiers is not supported.
Definitions of services are not supported, only messages.

The following limitations apply for the GPB support for proto3 in MediationZone:

The options that are supported are allow_alias in enums and packed for fields.
Importing definitions with the gpb_block is not supported.
Import public specifiers is not supported.
The parent message type is not supported.
The any type is not supported.
Packages are not supported.
Definitions of services are not supported, only messages.

Note!

The GPB message format is not self delimiting, which should be considered when decoding a stream of messages, or a file
containing several messages.

Nested Types in Proto3


Nested types are supported in proto3 in MediationZone. For further information on the specification for nested types, see https://developers.
google.com/protocol-buffers/docs/proto3. When referring to a nested type by using its qualified name, a point is used as delimiter, e.g. M1.
M2 or .M1.M2. However, note that while nested types are indicated with a point in the GPB specification, when mapping to Ultra and APL,
you must use an underscore instead. See the example provided below.

Example - Nested types in proto3

In this example M2 is nested inside M1. Depending on where you are inside the gbp_blockyou can refer to a message by its
relative nameM2 or its qualified name.M1.M2.

message M1 {
message M2 {
string f1 = 1;
}
M2 f2 = 2;

85
.M1.M2 f3 = 3;
}

The following shows how to map M1 to an internal configuration:

in_map M1_in: external(M1), target_internal(M1) {


automatic: use_external_names;
}

In APL you can refer to M1 and M2 by their qualified names M1 and M1_M2, where an underscore is used instead of a point.

udrCreate(M1);
udrCreate(M1_M2);

A GPB Format Example


To decode a GPB data file, a format definition is included in the Ultra gpb_block block in the Ultra format.

86
Example - GPB format using proto2

gpb_block {

message MyData{
required string myName =1;
required string myText =2;
required string extraName =3;

required int32 myPriority =4;


required uint32 myId =5;
required uint32 equipmentId =6;
required MyParam myParams = 7;
}

message MyParam {
repeated string someField = 1;
}

message MyAdditional {
required uint32 action = 1;
required string alias = 2;
required int64 content = 3;
optional int32 newId = 4;
optional int32 newType = 5;
optional uint64 myKey = 6;
}

message FlashEx {
required string someField = 1;
}

message MyExtras {
repeated FlashEx ex = 1;
}

message MyList {
repeated string list = 1;
}

message SysmanData {
required string someField = 1;
}
};

external Wrapper {
int dataSize: static_size(4), encode_value(udr_size-4);
SysmanData data : dynamic_size(dataSize);
};

in_map Wrapper_inMap: external(Wrapper),


target_internal(Wrapper_int) {
automatic;
};

out_map Wrapper_outMap: external(Wrapper),


internal(Wrapper_int) {
automatic;
};

decoder Wrapper_decoder: in_map(Wrapper_inMap);

encoder Wrapper_encoder: out_map(Wrapper_outMap);

Example - GPB format using proto3

gpb_block {
syntax = "proto3";

message MyData{
string myName =1;
string myText =2;
string extraName =3;

int32 myPriority =4;


uint32 myId =5;
uint32 equipmentId =6;
MyParam myParams = 7;
map<string, MyParam> myMap = 8;
AnEnum anEnum = 9;
AnotherEnum anotherEnum = 10;
}

enum AnEnum {
V0 = 0;
V1 = 1;
}
enum AnotherEnum {
option allow_alias = true;
V0 = 0;
V1 = 0;
}

message MyParam {
repeated string someField = 1;
}

message MyAdditional {
uint32 action = 1;
string alias = 2;
int64 content = 3;
int32 newId = 4;
int32 newType = 5;
uint64 myKey = 6;
}

message FlashEx {
string someField = 1;
}

message MyExtras {
repeated FlashEx ex = 1;
}

message MyList {
repeated string list = 1;
}

message SysmanData {
string someField = 1;
}
};

external Wrapper {
int dataSize: static_size(4), encode_value(udr_size-4);
SysmanData data : dynamic_size(dataSize);
};

in_map Wrapper_inMap: external(Wrapper),


target_internal(Wrapper_int) {
automatic;
};

out_map Wrapper_outMap: external(Wrapper),


internal(Wrapper_int) {
automatic;
};

decoder Wrapper_decoder: in_map(Wrapper_inMap);

encoder Wrapper_encoder: out_map(Wrapper_outMap);

Note!

Since GPB does not specify the size, this has to be done externally, which is why int dataSize has to be included in the
external unless the size is previously known.
20. Avro Support
This chapter describes the Avro support that has been added to the Ultra Format Definition Language (UFDL). This addition enables you to
compile Avro definitions, and to encode data into, and decode it from the Avro format. A schema in Avro is represented in JSON. You can
implement non-protocol data issues in an Analysis agent.

Overview
In MediationZone you manage Avro parsing in UFDL by applying the avro_block construct.

avro_block {
<avro json schema>

};

The output of an Avro encoder represents a single Avro record, not a complete Avro data file. To deserialize these records using a third
party tool, you must add the correct Avro header to the records based on the description of Avro provided in the link below.

The full description of the Avro language can be found at: https://avro.apache.org/docs/current/spec.html.

The Avro Types


The Avro JSON schema can be defined with any of the following types:

Note!

There are limitations to the implementation of the following elements. Refer to the the section below, Limitations, for further
information.

Primitive Type Notes

null Implemented as type byte

boolean Value must be true or false.

int 32-bit signed

long 64-bit signed

float Single precision (32-bit) IEEE 754 floating-point number

double Double precision (64-bit) IEEE 754 floating-point number

bytes Sequence of 8-bit unsigned bytes

string Unicode character sequence

Complex Notes
Type

record Supports the attributes: name, namespace, doc (optional), aliases (optional), fields.

fields is an array of listing fields and supports the attributes: name, doc (optional), type, default, order
(optional), aliases (optional).

enum Supports the attributes: name, namespace, doc (optional), aliases (optional), symbols.

array Supports the attribute: items

map Supports the attribute: values

union Implemented as a record with all types set to optional. If two values are set, both are encoded. If no values are set,
none are encoded.

To specify a union value, you must use udrCreate.

fixed

89
Supports the attributes: name, namespace, aliases (optional), size

Note!

Use the doc attribute to add a comment.

Limitations
The following limitations apply for the Avro support in MediationZone:

Line and Column are not fully implemented.


No string values are permitted in APL for enum type.
Encoding the Avro object container file is not supported.
aliases is not fully supported.
default is not fully supported.
Cross references to other Ultra definitions are not supported.
array of arrays is not correctly implemented. E g array of array of x is implemented with array of y, where y is a
record with one value field. This value field is an array of x.
map of x is implemented via an array of y, where y is a record with two fields: key and value, where value is x.

An Avro Format Example


To encode an Arvo data file, a format definition is included in the Ultra avro_block block in the Ultra format.

Example - The xml_schema Ultra Code Block

avro_block {
{
"namespace": "example.avro",
"type": "record",
"name": "User3",
"fields": [
{"name": "name", "type": {
"name": "FullName2",
"type": "record",
"fields": [
{"name": "firstName", "type": "string"},
{"name": "lastName", "type": "string"}
]
}

},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]},
{"name": "favorite_fotball_team", "type": {
"name": "teams",
"type": "enum",
"namespace": "example.avro.teams",
"symbols": ["Djurgården", "Hammarby", "Malmö", "Göteborg"]
}
},
{"name": "ipAddresses", "type": {
"type": "array",
"items": [
{
"name": "ipv4Address",
"type": "fixed",
"size": 4
},
{
"name": "ipv6Address",
"type": "fixed",
"size": 16
}
]
}
},
{"name": "favoriteFoodList", "type": {
"name": "favoriteFood",

90
"type": "record",
"fields": [
{"name": "dish", "type": "string"},
{"name": "next", "type": ["null", "favoriteFood"]}
]
}
},
{"name": "salary", "type": "long"},
{"name": "myFixed", "type": { "name": "myfixed", "type":"fixed", "size": 4 }},
{"name": "myFloat", "type": "float"},
{"name": "myDouble", "type": "double"},
{"name": "rootUsers", "type": {
"type": "map",
"values": {
"name": "RootUsers",
"type": "record",
"fields": [
{"name": "rootUser", "type": "string"},
{"name": "privileges", "type": "int"}
]
}
}
},
{"name": "maps", "type": {
"type": "array",
"items": {
"type": "map",
"values": {
"name": "Map2",
"type": "record",
"fields": [
{"name": "favoriteUser", "type": "string"},
{"name": "favoriteNumber", "type": "int"}
]
}
}
}
}
]
}
};

91
92

You might also like