TOP

ASN.1 Made Simple — Advanced Topics

Controlling XML Encoding

When XML encoding rules (XER or E-XER) are used, ASN.1 provides a set of encoding instructions to control the XML format. For example, to make the "currency" field to be an XML attribute, you can use this code:

UnitPrice ::= SEQUENCE {
unitPrice REAL,
currency [XER:ATTRIBUTE] VisibleString
}

Information Objects

ASN.1 has the ability to restrict the value of a component of a type definition to items from a "dictionary" (a reference table). It can also enforce relationships between items in such a dictionary.

For example, let's say that we have a list of products, each of them having a product code and a particular price. We would like to make sure that a message using this product code contains the correct description and price. In order to do this, we must first define a table containing "valid" kinds of products (product catalog), then define our products referencing this catalog. We use the CLASS keyword to define a reference table. In ASN.1 lingo, we will create an Information Object Class (we'll call it PRODUCT) and populate an Information Object Set (we'll call it ProductCatalog):

PRODUCT ::= CLASS {
&code        INTEGER (1..99999) UNIQUE,
&description VisibleString (SIZE (1..100)),
&price       REAL
} WITH SYNTAX { CODE &code , DESCRIPTION &description , PRICE &price}

ProductCatalog PRODUCT ::= {
{CODE 101, DESCRIPTION "iPhone v4", PRICE 250.00} |
{CODE 102, DESCRIPTION "Android Galaxy", PRICE 250.00} |
{CODE 103, DESCRIPTION "Win7 Nokia", PRICE 150.00}
}


Now, we redefine Item from the MyShopPurchaseOrders example to refer to the ProductCatalog table.

Item ::= SEQUENCE {
itemCode        PRODUCT.&code ({ProductCatalog}),
itemDescription PRODUCT.&description ({ProductCatalog}{@itemCode}),
quantity        INTEGER (1..1000),
unitPrice       PRODUCT.&price ({ProductCatalog}{@itemCode}),
itemTotal       REAL,
isTaxable       BOOLEAN
}

With these changes, if the CODE, DESCRIPTION and PRICE do not match an entry from ProductCatalog, a good ASN.1 tool should issue an error message.

Open Types

ASN.1 has the ability to allow one component to determine the ASN.1 type that another component will carry. The mechanism for this is called the open type. Let's say that we want to extend the class and the reference table defined above with a FEATURE to contain additional information about an item.

PRODUCT ::= CLASS {
&code        INTEGER (1..99999) UNIQUE,
&description VisibleString (SIZE (1..100)),
&price       REAL,
--OpenType-- &Feature    
} WITH SYNTAX { CODE &code , DESCRIPTION &description , PRICE &price , FEATURE &Feature }

ProductCatalog PRODUCT ::= {
{CODE 101, DESCRIPTION "iPhone", PRICE 250.00, FEATURE Generation } |
{CODE 102, DESCRIPTION "Android Galaxy", PRICE 250.00, FEATURE Generation } |
{CODE 103, DESCRIPTION "Win7 Nokia", PRICE 150.00, FEATURE Generation } |
{CODE 104, DESCRIPTION "Bookshelf", PRICE 100.00, FEATURE Weight} |
{CODE 105, DESCRIPTION "Glass Egg", PRICE 2000.00, FEATURE NULL}
}

The ASN.1 types mentioned in "FEATURE" need to be defined.

Generation ::= ENUMERATED {two-G, three-G, four-G}
Weight ::= INTEGER

We then add the open type component called "feature" to carry the additional information corresponding to the item chosen.

Item ::= SEQUENCE {
itemCode        PRODUCT.&code ({ProductCatalog }),
itemDescription PRODUCT.&description({ProductCatalog}{@itemCode}),
feature         PRODUCT.&Feature ({ProductCatalog}{@itemCode}),
quantity        INTEGER (1..1000),
unitPrice       PRODUCT.&price ({ProductCatalog}{@itemCode}),
itemTotal       REAL,
isTaxable       BOOLEAN
}

Note that "feature" must carry the information specified in the table for a given code. So if itemCode is 104, "feature" must contain a "Weight".

Versioning/Extensibility - Forward and Backward Compatibility

ASN.1 contains a mechanism for allowing multiple versions of a message to be exchanged without requiring all parties to be using the same version of the message. By including the ASN.1 extension mark in appropriate locations in a message, a protocol designer can ensure that releasing new versions of the protocol will not be disruptive to existing implementations. For example:

CustomerInfo ::= SEQUENCE {
companyName        VisibleString (SIZE (3..50)),
billingAddress     Address,
contactPhoneNumber NumericString (SIZE (7..12)),
...
}

We add the "..." to indicate that a new version may contain additional components, such as:

CustomerInfo ::= SEQUENCE {
companyName        VisibleString (SIZE (3..50)),
billingAddress     Address,
contactPhoneNumber NumericString (SIZE (7..12)),
...,
shippingAddress    Address
}

Someone using the first version can communicate with someone using the second version since the extension marker alerts both sides of possible new or missing components. Decoders are required to accept without error additional unknown components (such as shippingAddress), as well as accepting messages in which the additional components are missing (such as when version 2 receives a message from version 1).

Note that when version 3 comes, communication with a version 1 system or with a version 2 system is still fine:

CustomerInfo ::= SEQUENCE {
companyName        VisibleString (SIZE (3..50)),
billingAddress     Address,
contactPhoneNumber NumericString (SIZE (7..12)),
...,
shippingAddress    Address,
additionalInfo     VisibleString (SIZE (1..128))
}

The following ASN.1 types can be extended: ENUMERATED, SEQUENCE, SET, CHOICE. Information Object Sets are also extensible.

All types can have extensible constraints, but only constraints on the following types are PER-visible (meaning that the PER encodings are affected by the constraint):

  • INTEGER (all constraints)
  • BIT STRING (SIZE)
  • OCTET STRING (SIZE)
  • IA5String (SIZE and FROM)
  • PrintableString (SIZE and FROM)
  • VisibleString (SIZE and FROM)
  • NumericString (SIZE and FROM)
  • UniversalString (SIZE and FROM)
  • BMPString (SIZE and FROM)
  • SEQUENCE OF (SIZE)
  • SET OF (SIZE)
  • TIME types

Parameterization

All assignments that define reference names (types, values, value sets, information object classes, information objects, and information object sets) can be parameterized. Parameterized types can take multiple parameters. Also, parameters can be nested.

In the following example we have two dummy parameters - normal-priority and Parameter:

Invoke-message {INTEGER:normal-priority, Parameter} ::= SEQUENCE {
      component1 INTEGER DEFAULT normal-priority,
      component2 Parameter }

Now we define our messages as a choice of two possibilities that differ only in the default priority and the type that is to be used:

Messages ::= CHOICE {
      first Invoke-message { low-priority, Type1 },
      second Invoke-message { high-priority, Type2 },
      ... }
Messages ::= CHOICE { -- This is what the above expands to
      first SEQUENCE {
        component1 INTEGER DEFAULT low-priority,
        component2 Type1 },
      second SEQUENCE {
        component1 INTEGER DEFAULT high-priority,
        component2 Type2 },
      ... }