This document defines three DataEncodings: OPC UA Binary, OPC UA XML and OPC UA JSON. It describes how to construct Messages using each of these encodings.

All OPC UA DataEncodings are based on rules that are defined for a standard set of built-in types. These built-in types are then used to construct structures, arrays and Messages. The built-in types are described in Table 1.

Table 1 – Built-in Data Types

ID

Name

Nullable

Default

Description

1

Boolean

No

false

A two-state logical value (true or false).

2

SByte

No

0

An integer value between −128 and 127 inclusive.

3

Byte

No

0

An integer value between 0 and 255 inclusive.

4

Int16

No

0

An integer value between −32 768 and 32 767 inclusive.

5

UInt16

No

0

An integer value between 0 and 65 535 inclusive.

6

Int32

No

0

An integer value between −2 147 483 648 and 2 147 483 647 inclusive.

7

UInt32

No

0

An integer value between 0 and 4 294 967 295 inclusive.

8

Int64

No

0

An integer value between −9 223 372 036 854 775 808 and 9 223 372 036 854 775 807 inclusive.

9

UInt64

No

0

An integer value between 0 and 18 446 744 073 709 551 615 inclusive.

10

Float

No

0

An IEEE single precision (32 bit) floating point value.

11

Double

No

0

An IEEE double precision (64 bit) floating point value.

12

String

Yes

null

A sequence of Unicode characters.

13

DateTime

Yes

DateTime.MinValue (see 5.1.4)

An instance in time.

14

Guid

Yes

All zeros

A 16-byte value that can be used as a globally unique identifier.

15

ByteString

Yes

null

A sequence of octets.

16

XmlElement

Yes

null

A sequence of Unicode characters that is an XML element.

17

NodeId

Yes

All fields set to default.

An identifier for a node in the address space of an OPC UA Server.

18

ExpandedNodeId

Yes

All fields set to default.

A NodeId that allows the namespace URI to be specified instead of an index.

19

StatusCode

No

Good

A numeric identifier for an error or condition that is associated with a value or an operation.

20

QualifiedName

Yes

All fields set to default.

A name qualified by a namespace.

21

LocalizedText

Yes

All fields set to default.

Human readable text with an optional locale identifier.

22

ExtensionObject

Yes

All fields set to default.

A structure that contains an application specific data type that may not be recognized by the receiver.

23

DataValue

Yes

All fields set to default.

A data value with an associated status code and timestamps.

24

Variant

Yes

Null

A union of all of the types specified above.

25

DiagnosticInfo

Yes

No fields specified.

A structure that contains detailed error and diagnostic information associated with a StatusCode.

Most of these data types are the same as the abstract types defined in OPC 10000-3 and OPC 10000-4. However, the ExtensionObject and Variant types are defined in this document. In addition, this document defines a representation for the Guid type defined in OPC 10000-3.

The Nullable column indicates whether a ‘null’ value exists for the DataType in all DataEncodings. A ‘null’ value is a value that is equavalent ‘no value specified’. A nullable type with a default value means the default value shall be interpreted equivalent to a null.

The Default column specifies the default value for the type if a default value is needed. The default value for all arrays is ‘null’.

A Guid is a 16-byte globally unique identifier with the layout shown in Table 2.

Table 2 – Guid structure

Component

Data Type

Data1

UInt32

Data2

UInt16

Data3

UInt16

Data4

Byte [8]

Guid values may be represented as a string in this form:

<Data1>-<Data2>-<Data3>-<Data4[0:1]>-<Data4[2:7]>

where Data1 is 8 characters wide, Data2 and Data3 are 4 characters wide and each Byte in Data4 is 2 characters wide. Each value is formatted as a hexadecimal number with padded zeros. A typical Guid value would look like this when formatted as a string:

C496578A-0DFE-4B8F-870A-745238C6AEAE

DateTime values have different ranges on different DevelopmentPlatforms. To ensure interoperablity two named values are defined:

“DateTime.MinValue” is the earliest value that can be represented;

“DateTime.MaxValue” is the latest value that can be represented.

If the range supported by the DataEncoding is outside of the range supported by a DevelopmentPlatform then decoders shall replace any below range values with DateTime.MinValue and any above range values with DateTime.MaxValue for the DevelopmentPlatform.

If the range supported by a DevelopmentPlatform is outside of the range supported by a DataEncoding then encoders shall replace any below range values with DateTime.MinValue and any above range values with DateTime.MaxValue for the DataEncoding.

The representation of a DateTime on a DevelopmentPlatform also has a maximum precision. Decoders shall truncate DateTime values that exceed the supported precision. All DevelopmentPlatforms shall support a precision of at least 1ms.

The DataValue built-in type adds additional fields for Picoseconds. If a DevelopmentPlatform cannot support the full precision of DateTime values allowed by the DataEncoding then it should expand the size of its internal representation of Picoseconds field to preserve the full precision of the DateTime. If it does not do this it shall set the Picoseconds to 0.

The Picoseconds shall be set to 0 when the DateTime value is DateTime.MinValue or DateTime.MaxValue.

Concrete examples can be found in 5.2.2.5.

A ByteString is structurally the same as a one-dimensional array of Byte. It is represented as a distinct built-in data type because it allows encoders to optimize the transmission of the value. However, some DevelopmentPlatforms will not be able to preserve the distinction between a ByteString and a one-dimensional array of Byte.

If a decoder for DevelopmentPlatform cannot preserve the distinction it shall convert all one-dimensional arrays of Byte to ByteStrings.

Each element in a one-dimensional array of ByteString can have a different length which means is structurally different from a two-dimensional array of Byte where the length of each dimension is the same. This means decoders shall preserve the distinction between two or more dimension arrays of Byte and one or more dimension arrays of ByteString.

If a DevelopmentPlatform does not support unsigned integers, then it will have to represent ByteStrings as arrays of SByte. In this case, the requirements for Byte would then apply to SByte.

Number, Integer and UInteger are abstract simple types defined in OPC 10000-3. When these types are used in Structure fields, the field value is encoded as a Variant.

Structures are sequences of name value pairs defined by the DataTypeDefinition Attribute in OPC 10000-3. Each DataEncoding describes how to use the DataTypeDefinition to serialize Structures. Subtypes of Structure extend the parent by adding additional name-value pairs to the sequence.

If a DataTypeDefinition sets the StructureType to StructureWithSubtypedValues then any field with a subtype of Structure DataType and IsOptional =TRUE allows the type and any subtype of the field’s DataType to be present in the field. In these cases, the values are serialized as an ExtensionObject. If IsOptional =FALSE then the field value is encoded directly according to the rules for the DataEncoding.

A field is serialized as an ExtensionObject if a field has a DataType set explicitly to Structure.

Unions are special subtypes of Structure where only one field value is encoded. All subtypes of Union are concrete. The remainder of the rules for Structure also apply to Unions.

An ExtensionObject is a container for any Structure and Union DataTypes.

The ExtensionObject contains a complex value serialized as a sequence of other DataTypes. It also contains an identifier which indicates what data it contains and how it is encoded.

There are four primary use cases where ExtensionObjects appear:

In all of these cases, the ExtensionObject provides an identifier that allows a decoder to know if it understands the Structure contained with it and a length that allows the Structure to be skipped if it is not recognized.

Structured DataTypes are represented in a Server address space as sub-types of the Structure DataType. The DataEncodings available for any given Structured DataTypes are represented as a DataTypeEncoding Object in the Server AddressSpace. The NodeId for the DataTypeEncoding Object is the identifier stored in the ExtensionObject. OPC 10000-3 describes how DataTypeEncoding Nodes are related to other Nodes of the AddressSpace.

Elements of an array of ExtensionObjects may have different DataTypeEncoding NodeIds specified. In some cases, this will be invalid, however, it is the responsibility of the application layer to enforce whatever constraints are imposed by the Information Model on a given array. Decoders shall accept any valid ExtensionObject as an array element.

Server implementers should use namespace qualified numeric NodeIds for any DataTypeEncoding Objects they define. This will minimize the overhead introduced by packing Structured DataType values into an ExtensionObject.

ExtensionObjects and Variants allow unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.

A Variant is a union of all built-in data types including an ExtensionObject. Variants can also contain arrays of any of these built-in types. Variants are used to store any value or parameter with a data type of BaseDataType or one of its subtypes.

Variants can be empty. An empty Variant is described as having a null value and should be treated like a null column in a SQL database. A null value in a Variant may not be the same as a null value for data types that support nulls such as Strings. Some DevelopmentPlatforms may not be able to preserve the distinction between a null for a DataType and a null for a Variant, therefore, applications shall not rely on this distinction. This requirement also means that if an Attribute supports the writing of a null value it shall also support writing of an empty Variant and vice versa.

Variants can contain arrays of Variants but they cannot directly contain another Variant.

DiagnosticInfo types only have meaning when returned in a response message with an associated StatusCode and table of strings. As a result, Variants cannot contain instances of DiagnosticInfo.

Values of Attributes are always returned in instances of DataValues. Therefore, the DataType of an Attribute cannot be a DataValue. Variants can contain DataValue when used in other contexts such as Method Arguments or PubSub Messages. The Variant in a DataValue cannot, directly or indirectly, contain another DataValue.

ExtensionObjects and Variants allow unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.

A Decimal is a high-precision signed decimal number. It consists of an arbitrary precision integer unscaled value and an integer scale. The scale is the inverse power of ten that is applied to the unscaled value.

A Decimal has the fields described in Table 3.

Table 3 – Layout of Decimal

Field

Type

Description

TypeId

NodeId

The identifier for the Decimal DataType.

Encoding

Byte

This value is always 1.

Length

Int32

The length of the Scale and Value fields in bytes.If the length is less than or equal to 2 then the Decimal is an invalid value that cannot be used.

Scale

Int16

A signed integer representing scale which is the inverse power of ten that is applied to the unscaled value.

i.e., the decimal number of the value multiplied by 10-scale

The integer is encoded starting with the least significant bit.

Value

OctetString

A 2-complement signed integer representing the unscaled value.

The number of bytes is the value of the Length field minus size of the Scale field.

The integer is encoded with the least significant byte first.

When a Decimal is encoded in a Variant the built-in type is set to ExtensionObject. Decoders that do not understand the Decimal type shall treat it like any other unknown Structure and pass it on to the application. Decoders that do understand the Decimal can parse the value and use any construct that is suitable for the DevelopmentPlatform. Note that a Decimal is like a built-in type and a DevelopmentPlatform has to have hardcoded knowledge of the type. No Structure metadata is published for this type.

If a Decimal is embedded in another Structure then the DataTypeDefinition for the field shall specify the NodeId of the Decimal Node as the DataType. If a Server publishes an OPC Binary type description for the Structure then the type description shall set the DataType for the field to ExtensionObject.

The terms null, empty and zero-length are used to describe array values (Strings are arrays of characters and ByteStrings are arrays of Bytes for purposes of this discussion). A null array has no value. A zero-length or empty array is an array with 0 elements. Some DataEncodings will allow the distinction to be preserved on the wire, however, not all DevelopmentPlatforms will be able to preserve the distinction. For this reason, null, empty and zero length arrays are semantically the same for all DataEncodings. Decoders shall be able to handle all variations supported by the DataEncoding, however, decoders are not required to preserve the distinction. When testing for equality, applications shall treat null and empty arrays as equal. When a DevelopmentPlatform supports the distinction, writing and reading back an array value may result in null array becoming an empty array or vice versa.

QualifiedNames, NodeIds and ExpandedNodeIds optimize representation of NamespaceUris by using a NamespaceIndex to reference the location of the URI in a NamespaceTable. The NamespaceTable to use is known from the context. For example, when a Client establishes a Session with a Server, the Server supplies the NamespaceTable that is used for all exchanges within that Session. Another example, is when a Publisher publishes DataSetMetadata messages, the NamespaceTable is provided as part of the message.

However, there are other scenarios where there is no obvious context that can be used to store the NamespaceTable, such as column in a database table, so it is necessary to provide a self-contained representation of these DataTypes. This clause defines a normative String representation using the ABNF notation (see RFC 5234). Table 4 defines additional core rules used in these definitions.

Table 4 – Additional ABNF Core Rules

UNICODE

Any Unicode character other than a Control character.

CONTROL

Any Unicode Control character (includes nulls, carriage returns, tabs and new lines).

URI

A string that conforms to RFC 3986.

ENCODEDURI

A URI which has the RFC 3986 Percent-Encoding applied to it.

BASE64

A Base64 encoded binary value (see Base64).

The ABNF description for a NodeId is found in Table 5.

Table 5 – ABNF Description for a NodeId

<node-id>

= <identifier>

<node-id>

=/ <namespace-index> ";" <identifier>

<node-id>

=/ <namespace-uri> ";" <identifier>

<namespace-index>

= "ns=" *DIGIT

<namespace-uri>

= "nsu=" ENCODEDURI

<identifier>

= <numericid> / <stringid> / <guidid> / <opaqueid>

<numericid>

= "i=" *DIGIT

<stringid>

= "s=" *(UNICODE)

<guidid>

= "g=" 8HEXDIG 3("-" 4HEXDIG) "-" 12HEXDIG

<opaqueid>

= "b=" BASE64

<reserved>

= ";" / "%" / CONTROL

<escaped>

= "%" 2HEXDIG

NodeIds with a NamespaceIndex of 0 or a NamespaceUri of http://opcfoundation.org/UA/ shall only use the <identifier> form.

The <escaped> form of a Unicode character is the UTF-8 encoding of the Unicode character represented as a “%” followed by a hexadecimal number.Examples of NodeIds:

i=13

ns=10;i=12345

nsu=http://widgets.com/schemas/hello;s=水%09World

g=09087e75-8e5e-499b-954f-f2a9603db28a

nsu=tag:acme.com,2023:schemas:data#off%3B;b=M/RbKBsRVkePCePcx24oRA==

The ABNF description for a ExpandedNodeId is found in Table 6.

Table 6 – ABNF Description for a ExpandedNodeId

<expanded-node-id>

= <node-id>

<expanded-node-id>

=/ <server-index> ";" <node-id>

<expanded-node-id>

=/ <server-uri> ";" <node-id>

<server-index>

= "svr=" *DIGIT

<server-uri>

= "svu=" ENCODEDURI

ExpandedNodeIds that are not specific to a Server shall use the <node-id> form.

Examples of ExpandedNodeIds:

i=13

svr=1;nsu=http://widgets.com/schemas/hello;s=水%09World

svu=http://smith.com/east/factory;g=09087e75-8e5e-499b-954f-f2a9603db28a

svu=http://smith.com/west/factory;nsu=tag:acme.com,2023:schemas:data#off%3B;b=M/RbKBsRVkePCePcx24oRA==

The ABNF description for a QualifiedName is found in Table 7.

Table 7 – ABNF Description for a QualifiedName

<qualified-name>

= <name>

<qualified-name>

=/ <namespace-index> ":" <name>

<qualified-name>

=/ <namespace-uri> ";" <name>

<name>

= *(UNICODE)

QualifiedNames in the OPC UA Namespace shall not use the forms with a <namespace-index> or a <namespace-uri>.

Examples of QualifiedNames:

InputArguments

3:Hello%09World

nsu=http://widgets.com/schemas/hello;Hello%3BWorld

nsu=tag:acme.com,2023:schemas:data#off%3B;Boiler2

Text based DataEncodings such as UA XML (5.3) or UA JSON (5.4) make use of Names from the DataType and DataTypeDefinition to create the serialized data. XML and JSON formats impose restrictions on the Strings that may be used in serialized data so it is necessary to define a transformation that ensures any Names can be used in the DataEncoding. The following rules are used to convert a DataType Name or a Structure Field Name to a string supported by the Encoding:

  1. Any character that is not permitted by the DataEncoding is replaced by an underscore (U+005F);
  2. A name with text sequence that is not valid in the first position has an underscore (U+005F) added as a prefix.

The character restrictions for the XML DataEncoding are:

<allowed-name>

= <letter> *(<allowed-char> / "_" / "-"/ ".")

<allowed-char>

= <letter> / DIGIT

<letter>

= UNICODE-LETTER

UNICODE-LETTER

Any Unicode character with a general category of ‘Letter’

In addition, XML names cannot start with the text ‘xml’ or any variation in case (i.e. ‘xMl’).

There are no restrictions for the JSON encoding other than the general restrictions defined in OPC 10000-3.

Table 8 has examples of XML encoded names.

Table 8 – Examples of XML Encoded Names

Hello

Hello

_Hello

_Hello

3DHello

_3DHello

冷水

冷水

3 (冷水。)-Hello

_3__冷水___-Hello

The OPC UA Binary DataEncoding is a data format developed to meet the performance needs of OPC UA applications. This format is designed primarily for fast encoding and decoding, however, the size of the encoded data on the wire was also a consideration.

The OPC UA Binary DataEncoding relies on several primitive data types with clearly defined encoding rules that can be sequentially written to or read from a binary stream. A structure is encoded by sequentially writing the encoded form of each field. If a given field is also a structure, then the values of its fields are written sequentially before writing the next field in the containing structure. All fields shall be written to the stream even if they contain null values. The encodings for each primitive type specify how to encode either a null or a DefaultValue for the type.

The OPC UA Binary DataEncoding does not include any type or field name information because all OPC UA applications are expected to have advance knowledge of the services and structures that they support. An exception is an ExtensionObject which provides an identifier and a size for the Structured DataType structure it represents. This allows a decoder to skip over types that it does not recognize.

A Boolean value shall be encoded as a single byte where a value of 0 (zero) is false and any non-zero value is true.

Encoders shall use the value of 1 to indicate a true value; however, decoders shall treat any non-zero value as true.

All integer types shall be encoded as little-endian values where the least significant byte appears first in the stream.

Figure 2 illustrates how value 1 000 000 000 (Hex: 3B9ACA00) is encoded as a 32-bit integer in the stream.

image005.png

Figure 2 – Encoding Integers in a binary stream

All floating-point values shall be encoded with the appropriate IEEE 754 binary representation which has three basic components: the sign, the exponent, and the fraction. The bit ranges assigned to each component depend on the width of the type. Table 9 lists the bit ranges for the supported floating-point types.

Table 9 – Supported Floating Point Types

Name

Width (bits)

Fraction

Exponent

Sign

Float

32

0-22

23-30

31

Double

64

0-51

52-62

63

In addition, the order of bytes in the stream is significant. All floating-point values shall be encoded with the least significant byte appearing first (i.e., little endian).

Figure 3 illustrates how the value −6,5 (Hex: C0D00000) is encoded as a Float.

The floating-point type supports positive and negative infinity and not-a-number (NaN). The IEEE specification allows for multiple NaN variants; however, the encoders/decoders may not preserve the distinction. Encoders shall encode a NaN value as an IEEE quiet-NAN (Hex: 000000000000F8FF) or (Hex: 0000C0FF). Any unsupported types such as denormalized numbers shall also be encoded as an IEEE quiet-NAN. Any test for equality between NaN values always fails.

image006.png

Figure 3 – Encoding Floating Points in a binary stream

All String values are encoded as a sequence of UTF-8 characters preceded by the length in bytes.

The length in bytes is encoded as Int32. A value of −1 is used to indicate a ‘null’ string.

Strings with embedded nulls (‘\0’) can lead to unpredictable application behaviour because embedded nulls have special meaning to some DevelopmentPlatforms. For this reason, embedded nulls are not recommended and ByteString should be used instead. That said, Encoders may encode Strings with embedded nulls. Decoders shall use the length to read all bytes in String.

Figure 4 illustrates how the multilingual string ‘水Boy’ is encoded in a byte stream.

image007.png

Figure 4 – Encoding Strings in a binary stream

A DateTime value shall be encoded as a 64-bit signed integer (see 5.2.2.2) which represents the number of 100 nanosecond intervals since January 1, 1601 (UTC).

Not all DevelopmentPlatforms will be able to represent the full range of dates and times that can be represented with this DataEncoding. For example, the UNIX time_t structure only has a 1 second resolution and cannot represent dates prior to 1970. For this reason, a number of rules shall be applied when dealing with date/time values that exceed the dynamic range of a DevelopmentPlatform. These rules are:

  1. A date/time value is encoded as 0 if either
  2. The value is equal to or earlier than 1601-01-01 12:00AM UTC.
  3. The value is the earliest date that can be represented with the DevelopmentPlatform’s encoding.
  4. A date/time is encoded as the maximum value for an Int64 if either
  5. The value is equal to or greater than 9999-12-31 11:59:59PM UTC,
  6. The value is the latest date that can be represented with the DevelopmentPlatform’s encoding.
  7. A date/time is decoded as the earliest time that can be represented on the platform if either
  8. The encoded value is 0,
  9. The encoded value represents a time earlier than the earliest time that can be represented with the DevelopmentPlatform’s encoding.
  10. A date/time is decoded as the latest time that can be represented on the platform if either
  11. The encoded value is the maximum value for an Int64,
  12. The encoded value represents a time later than the latest time that can be represented with the DevelopmentPlatform’s encoding.

These rules imply that the earliest and latest times that can be represented on a given platform are invalid date/time values and should be treated that way by applications.

A decoder shall truncate the value if a decoder encounters a DateTime value with a resolution that is greater than the resolution supported on the DevelopmentPlatform.

A Guid is encoded in a structure as shown in Table 2. Fields are encoded sequentially according to the data type for field.

Figure 5 illustrates how the Guid “72962B91-FA75-4AE6-8D28-B404DC7DAF63” is encoded in a byte stream.

image008.png

Figure 5 – Encoding Guids in a binary stream

A ByteString is encoded as sequence of bytes preceded by its length in bytes. The length is encoded as a 32-bit signed integer as described above.

If the length of the byte string is −1 then the byte string is ‘null’.

An XmlElement is an XML element serialized as UTF-8 string and then encoded as ByteString.

Figure 6 illustrates how the XmlElement “<A>Hot水</A>” is encoded in a byte stream.

. image009.png

Figure 6 – Encoding XmlElement in a binary stream

A decoder may choose to parse the XML after decoding; if an unrecoverable parsing error occurs then the decoder should try to continue processing the stream. For example, if the XmlElement is the body of a Variant or an element in an array which is the body of a Variant then this error can be reported by setting value of the Variant to the StatusCode Bad_DecodingError.

The components of a NodeId are described the Table 10.

Table 10 – NodeId components

Name

Data Type

Description

Namespace

UInt16

The index for a namespace URI.

An index of 0 is used for OPC UA defined NodeIds.

IdentifierType

Enumeration

The format and data type of the identifier.

The value may be one of the following:

NUMERIC - the value is an UInteger;

STRING - the value is String;

GUID - the value is a Guid;

OPAQUE- the value is a ByteString;

Value

UInt32 or String or Guid or ByteString

The identifier for a node in the address space of an OPC UA Server.

The DataEncoding of a NodeId varies according to the contents of the instance. For that reason, the first byte of the encoded form indicates the format of the rest of the encoded NodeId. The possible DataEncoding formats are shown in Table 11. Table 11 through Table 14 describe the structure of each possible format (they exclude the byte which indicates the format).

Table 11 – NodeId DataEncoding values

Name

Value

Description

Two Byte

0x00

A numeric value that fits into the two-byte representation.

Four Byte

0x01

A numeric value that fits into the four-byte representation.

Numeric

0x02

A numeric value that does not fit into the two or four byte representations.

String

0x03

A String value.

Guid

0x04

A Guid value.

ByteString

0x05

An opaque (ByteString) value.

NamespaceUri Flag

0x80

See discussion of ExpandedNodeId in 5.2.2.10.

ServerIndex Flag

0x40

See discussion of ExpandedNodeId in 5.2.2.10.

The standard NodeId DataEncoding has the structure shown in Table 12. The standard DataEncoding is used for all formats that do not have an explicit format defined.

Table 12 – Standard NodeId Binary DataEncoding

Name

Data Type

Description

Namespace

UInt16

The NamespaceIndex.

Identifier

*

The identifier which is encoded according to the following rules:

NUMERIC

UInt32

STRING

String

GUID

Guid

OPAQUE

ByteString

An example of a String NodeId with Namespace = 1 and Identifier = “Hot水” is shown in Figure 7.

image010.png

Figure 7 – A String NodeId

The Two Byte NodeId DataEncoding has the structure shown in Table 13.

Table 13 – Two Byte NodeId Binary DataEncoding

Name

Data Type

Description

Identifier

Byte

The Namespace is the default OPC UA namespace (i.e. 0).

The Identifier Type is ‘Numeric’.

The Identifier shall be in the range 0 to 255.

An example of a Two Byte NodeId with Identifier = 72 is shown in Figure 8.

image011.png

Figure 8 – A Two Byte NodeId

The Four Byte NodeId DataEncoding has the structure shown in Table 14.

Table 14 – Four Byte NodeId Binary DataEncoding

Name

Data Type

Description

Namespace

Byte

The Namespace shall be in the range 0 to 255.

Identifier

UInt16

The Identifier Type is ‘Numeric’.

The Identifier shall be an integer in the range 0 to 65 535.

An example of a Four Byte NodeId with Namespace = 5 and Identifier = 1 025 is shown in Figure 9.

image012.png

Figure 9 – A Four Byte NodeId

An ExpandedNodeId extends the NodeId structure by allowing the NamespaceUri to be explicitly specified instead of using the NamespaceIndex. The NamespaceUri is optional. If it is specified, then the NamespaceIndex inside the NodeId shall be ignored.

The ExpandedNodeId is encoded by first encoding a NodeId as described in 5.2.2.9 and then encoding NamespaceUri as a String.

An instance of an ExpandedNodeId may still use the NamespaceIndex instead of the NamespaceUri. In this case, the NamespaceUri is not encoded in the stream. The presence of the NamespaceUri in the stream is indicated by setting the NamespaceUri flag in the encoding format byte for the NodeId.

If the NamespaceUri is present, then the encoder shall encode the NamespaceIndex as 0 in the stream when the NodeId portion is encoded. The unused NamespaceIndex is included in the stream for consistency.

An ExpandedNodeId may also have a ServerIndex which is encoded as a UInt32 after the NamespaceUri. The ServerIndex flag in the NodeId encoding byte indicates whether the ServerIndex is present in the stream. The ServerIndex is omitted if it is equal to zero.

The ExpandedNodeId encoding has the structure shown in Table 15.

Table 15 – ExpandedNodeId Binary DataEncoding

Name

Data Type

Description

NodeId

NodeId

The NamespaceUri and ServerIndex flags in the NodeId encoding indicate whether those fields are present in the stream.

NamespaceUri

String

Not present if null or Empty.

ServerIndex

UInt32

Not present if 0.

A StatusCode is encoded as a UInt32.

A DiagnosticInfo structure is described in OPC 10000-4. It specifies a number of fields that could be missing. For that reason, the encoding uses a bit mask to indicate which fields are actually present in the encoded form.

As described in OPC 10000-4, the SymbolicId, NamespaceUri, LocalizedText and Locale fields are indexes in a string table which is returned in the response header. Only the index of the corresponding string in the string table is encoded. An index of −1 indicates that there is no value for the string.

DiagnosticInfo allows unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.

Table 16 – DiagnosticInfo Binary DataEncoding

Name

Data Type

Description

Encoding Mask

Byte

A bit mask that indicates which fields are present in the stream.

The mask has the following bits:

0x01

Symbolic Id

0x02

Namespace

0x04

LocalizedText

0x08

Locale

0x10

Additional Info

0x20

InnerStatusCode

0x40

InnerDiagnosticInfo

SymbolicId

Int32

A symbolic name for the status code.

NamespaceUri

Int32

A namespace that qualifies the symbolic id.

Locale

Int32

The locale used for the localized text.

LocalizedText

Int32

A human readable summary of the status code.

Additional Info

String

Detailed application specific diagnostic information.

Inner StatusCode

StatusCode

A status code provided by an underlying system.

Inner DiagnosticInfo

DiagnosticInfo

Diagnostic info associated with the inner status code.

A QualifiedName structure is encoded as shown in Table 17.

The abstract QualifiedName structure is defined in OPC 10000-3.

Table 17 – QualifiedName Binary DataEncoding

Name

Data Type

Description

NamespaceIndex

UInt16

The namespace index.

Name

String

The name.

A LocalizedText structure contains two fields that could be missing. For that reason, the encoding uses a bit mask to indicate which fields are actually present in the encoded form.

The abstract LocalizedText structure is defined in OPC 10000-3.

Table 18 – LocalizedText Binary DataEncoding

Name

Data Type

Description

EncodingMask

Byte

A bit mask that indicates which fields are present in the stream.

The mask has the following bits:

0x01

Locale

0x02

Text

Locale

String

The locale.

Omitted is null or empty.

Text

String

The text in the specified locale.

Omitted is null or empty.

An ExtensionObject is encoded as sequence of bytes prefixed by the NodeId of its DataTypeEncoding, the DataEncoding used and the number of bytes encoded.

An ExtensionObject may be serialized as a ByteString or an XmlElement by the application and then passed to the encoder. In this case, the encoder will be able to write the number of bytes in the object before it encodes the bytes. However, an ExtensionObject may know how to encode/decode itself which means the encoder shall calculate the number of bytes before it encodes the object or it shall be able to seek backwards in the stream and update the length after encoding the body.

When a decoder encounters an ExtensionObject it shall check if it recognizes the DataTypeEncoding identifier. If it does, then it can call the appropriate function to decode the object body. If the decoder does not recognize the type it shall use the Encoding to determine if the body is a ByteString or an XmlElement and then decode the object body or treat it as opaque data and skip over it.

The serialized form of an ExtensionObject is shown in Table 19.

Table 19 – Extension Object Binary DataEncoding

Name

Data Type

Description

TypeId

NodeId

The identifier for the DataTypeEncoding node in the Server's AddressSpace. ExtensionObjects defined by the OPC UA specification have a numeric node identifier assigned to them with a NamespaceIndex of 0. The numeric identifiers are defined in A.3.

Decoders use this field to determine the syntax of the Body. For example, if this field is the NodeId of the JSON Encoding Object for a DataType then the Body is a ByteString containing a JSON document encoded as a UTF-8 string.

Encoding

Byte

An enumeration that indicates how the body is encoded.

The parameter may have the following values:

0x00

No body is encoded.

0x01

The body is encoded as a ByteString.

0x02

The body is encoded as an XmlElement.

Length

Int32

The length of the object body.

The length shall be specified if the body is encoded.

Body

OctetString

The encoded object.

This field contains the raw bytes for ByteString bodies.

For XmlElement bodies this field contains the XML encoded as a UTF-8 string without any null terminator.

Some binary encoded structures may have a serialized length that is not a multiple of 8 bits. Encoders shall append 0 bits to ensure the serialized length is a multiple of 8 bits. Decoders that understand the serialized format shall ignore the padding bits.

A decoder may choose to parse an XmlElement body after decoding; if an unrecoverable parsing error occurs then the decoder should try to continue processing the stream. For example, if the ExtensionObject is the body of a Variant or an element in an array that is the body of Variant then this error can be reported by setting the value of the Variant to the StatusCode Bad_DecodingError.

A Variant is a union of the built-in types.

The structure of a Variant is shown in Table 20.

Table 20 – Variant Binary DataEncoding

Name

Data Type

Description

EncodingMask

Byte

The type of data encoded in the stream. A value of 0 specifies a NULL and that no other fields are encoded.The mask has the following bits assigned:

0:5

Built-in Type Id (see Table 1).

6

True if the ArrayDimensions field is encoded.

7

True if an array of values is encoded.

The Built-in Type Ids 26 through 31 are not currently assigned but may be used in the future. Decoders shall accept these IDs, assume the Value contains a ByteString or an array of ByteStrings and pass both onto the application. Encoders shall not use these IDs.

The ArrayDimensions field shall only be present if the number of dimensions is 2 or greater and all dimensions have a length greater than 0.

ArrayLength

Int32

The number of elements in the array.

This field is only present if the array bit is set in the encoding mask.

Multi-dimensional arrays are encoded as a one-dimensional array and this field specifies the total number of elements. The original array can be reconstructed from the dimensions that are encoded after the value field.

Higher rank dimensions are serialized first. For example, an array with dimensions [2,2,2] is written in this order:

[0,0,0], [0,0,1], [0,1,0], [0,1,1], [1,0,0], [1,0,1], [1,1,0], [1,1,1]

If one or more dimensions has a length <= 0 then the ArrayLength is 0.

Value

*

The value encoded according to its built-in data type.

If the array bit is set in the encoding mask, then each element in the array is encoded sequentially. Since many types have variable length encoding each element shall be decoded in order.

The value shall not be a Variant but it could be an array of Variants.

Many implementation platforms do not distinguish between one dimensional Arrays of Bytes and ByteStrings. For this reason, decoders are allowed to automatically convert an Array of Bytes to a ByteString.

ArrayDimensionsLength

Int32

The number of dimensions.

This field is only present if the ArrayDimensions flag is set in the encoding mask.

ArrayDimensions

Int32[]

The length of each dimension encoded as a sequence of Int32 values

This field is only present if the ArrayDimensions flag is set in the encoding mask. The lower rank dimensions appear first in the array.

All dimensions shall be specified and shall be greater than zero..

If ArrayDimensions are inconsistent with the ArrayLength then the decoder shall stop and raise a Bad_DecodingError.

The types and their identifiers that can be encoded in a Variant are shown in Table 1.

A DataValue is always preceded by a mask that indicates which fields are present in the stream.

The fields of a DataValue are described in Table 21.

Table 21 – Data Value Binary DataEncoding

Name

Data Type

Description

Encoding Mask

Byte

A bit mask that indicates which fields are present in the stream.

The mask has the following bits:

0x01

False if the Value is Null.

0x02

False if the StatusCode is Good.

0x04

False if the SourceTimestamp is DateTime.MinValue.

0x08

False if the ServerTimestamp is DateTime.MinValue.

0x10

False if the SourcePicoseconds is not present.

0x20

False if the ServerPicoseconds is not present.

Value

Variant

The value.

Not present if the Value bit in the EncodingMask is False.

Status

StatusCode

The status associated with the value.

Not present if the StatusCode bit in the EncodingMask is False.

SourceTimestamp

DateTime

The source timestamp associated with the value.

Not present if the SourceTimestamp bit in the EncodingMask is False.

SourcePicoseconds

UInt16

The number of 10 Picosecond intervals for the SourceTimestamp.

Not present if the SourcePicoseconds bit in the EncodingMask is False.

If the source timestamp is missing the Picoseconds are ignored.

ServerTimestamp

DateTime

The Server timestamp associated with the value.

Not present if the ServerTimestamp bit in the EncodingMask is False.

ServerPicoseconds

UInt16

The number of 10 Picosecond intervals for the ServerTimestamp.

Not present if the ServerPicoseconds bit in the EncodingMask is False.

If the Server timestamp is missing the Picoseconds are ignored.

The Picoseconds fields store the difference between a high-resolution timestamp with a resolution of 10 Picoseconds and the Timestamp field value which only has a 100 ns resolution. The Picoseconds fields shall contain values less than 10 000. The decoder shall treat values greater than or equal to 10 000 as the value ‘9999’.

Decimals are encoded as described in 5.1.10.

A Decimal does not have a NULL value.

Enumerations are encoded as Int32 values.

An Enumeration does not have a NULL value.

One dimensional Arrays are encoded as a sequence of elements preceded by the number of elements encoded as an Int32 value.

Multi-dimensional Arrays have an encoding that depends on where they are used. When a multi-dimensional Array is the Value of an Attribute it uses the Variant encoding described in 5.2.2.16.

When a multi-dimensional Array is a field of a Structure (see 5.2.6) it shall be encoded with the inline matrix representation as shown in Table 22.

Table 22 – Inline Matrix DataEncoding

Name

Data Type

Description

Dimensions

Int32 []

The length of each dimension.

If any dimension has a length <= 0, then no values are encoded.

The number of dimensions shall be at least 2.

Values

*

The values encoded sequentially according to its built-in data type.

The total number of values is the product of the dimensions.

The mapping of a multidimensional array to a flat list is described in 5.2.2.16.

The inline matrix representation is not supported by earlier versions of this specification. This means any Structure with a field or nested field mapped to an inline matrix is not compatible with the deprecated DataTypeDictionary mechanism, and shall not be included in a DataTypeDictionary.

If an Array is null, then its length is encoded as −1. See 5.1.11 for a discussion of zero-length vs null arrays.

Structures are encoded as a sequence of fields in the order that they appear in the definition. The encoding for each field is determined by the built-in type for the field.

All fields specified in the structure shall be encoded. If optional fields exist in the Structure, then see 5.2.7.

Structures do not have a null value. If an encoder is written in a programming language that allows structures to have null values, then the encoder shall create a new instance with DefaultValues for all fields and serialize that. Encoders shall not generate an encoding error in this situation.

The following is an example of a structure using C/C++ syntax:

struct Type2

{

Int32 A;

Int32 B;

};

struct Type1

{

Int32 X;

Byte NoOfY;

Type2* Y;

Int32 Z;

UInt16 W[10];

Byte M[2,3,4];

};

In the C/C++ example above, the Y field is a pointer to an array with a length stored in NoOfY. When encoding an array, the length is part of the array encoding so the NoOfY field is not encoded. That said, encoders and decoders use NoOfY during encoding. W is a fixed length array with an implicitly defined length of 10. This length is always encoded with the array. M is a fixed length multidimensional array. The length of each dimension is always encoded with the array.

An instance of Type1 which contains an array of two Type2 instances would be encoded as 28-byte sequence. If the instance of Type1 was encoded in an ExtensionObject it would have an additional prefix shown in Table 23 which would make the total length 101 bytes The TypeId, Encoding and the Length are fields defined by the ExtensionObject. The encoding of the Type2 instances do not include any type identifier because it is explicitly defined in Type1.

Table 23 – Sample OPC UA Binary Encoded structure

Field

Bytes

Value

Type Id

4

The identifier for the ‘Type1’ Binary Encoding Node

Encoding

1

0x1 for ByteString

Length

4

28

X

4

The value of field ‘X’

Y.Length

4

2

Y.A

4

The value of field ‘Y[0].A’

Y.B

4

The value of field ‘Y[0].B’

Y.A

4

The value of field ‘Y[1].A’

Y.B

4

The value of field ‘Y[1].B’

Z

4

The value of field ‘Z’

W.Length

4

10

W

20

The value of field ‘W’.

M.Dimensions.Length

4

3

M.Dimensions

12

The sequence [2,3,4] encoded as a Int32.

M.Values

24

The values in ‘M’ encoded sequentially as described in 5.2.2.16.

The Value of the DataTypeDefinition Attribute for a DataType Node describing Type1 is shown in Table 24.

Table 24 – DataTypeDefinition for “Type1” from Sample

Name

Type

Description

defaultEncodingId

NodeId

NodeId of the “Type1_Encoding_DefaultBinary” Node.

baseDataType

NodeId

“i=22” [Structure]

structureType

StructureType

Structure_0 [Structure without optional fields]

fields [0]

StructureField

name

String

“X”

description

LocalizedText

Description of X

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [1]

StructureField

name

String

“Y“

description

LocalizedText

Description of Y-Array

dataType

NodeId

NodeId of the Type2 DataType Node (e.g. “ns=3; s=MyType2”)

valueRank

Int32

1 (OneDimension)

arrayDimensions

UInt32[]

{ 0 }

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [2]

StructureField

name

String

“Z“

description

LocalizedText

Description of Z

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [3]

StructureField

name

String

“W“

description

LocalizedText

Description of W

dataType

NodeId

“i=5” [UInt16]

valueRank

Int32

1 (OneDimension)

arrayDimensions

UInt32[]

{ 10 }

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [4]

StructureField

name

String

“M“

description

LocalizedText

Description of M

dataType

NodeId

“i=3” [Byte]

valueRank

Int32

3

arrayDimensions

UInt32[]

{ 2, 3, 4 }

maxStringLength

UInt32

0

isOptional

Boolean

false

The Value of the DataTypeDefinition Attribute for a DataType Node describing Type2 is shown in Table 25.

Table 25 –DataTypeDefinition for “Type2” from Sample

Name

Type

Description

defaultEncodingId

NodeId

NodeId of the “Type2_Encoding_DefaultBinary” Node.

baseDataType

NodeId

“i=22” [Structure]

structureType

StructureType

Structure_0 [Structure without optional fields]

fields [0]

StructureField

name

String

“A“

description

LocalizedText

Description of A

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [1]

StructureField

name

String

“B“

description

LocalizedText

Description of B

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

Structures with optional fields are encoded with an encoding mask preceding a sequence of fields in the order that they appear in the definition. The encoding for each field is determined by the data type for the field.

The EncodingMask is a 32-bit unsigned integer. Each optional field is assigned exactly one bit. The first optional field is assigned bit ‘0’, the second optional field is assigned bit ‘1’ and so until all optional fields are assigned bits. A maximum of 32 optional fields can appear within a single Structure. Unassigned bits are set to 0 by encoders. Decoders shall report an error if unassigned bits are not 0.

The following is an example of a structure with optional fields using C++ syntax:

struct TypeA

{

Int32 X;

Int32* O1;

SByte Y;

Int32* O2;

};

O1 and O2 are optional fields which are NULL if not present

An instance of TypeA which contains two mandatory (X and Y) and two optional (O1 and O2) fields would be encoded as a byte sequence. The length of the byte sequence depends on the available optional fields. An encoding mask field determines the available optional fields.

An instance of TypeA where field O2 is available and field O1 is not available would be encoded as a 13-byte sequence. If the instance of TypeA was encoded in an ExtensionObject it would have the encoded form shown in Table 26 and have a total length of 22 bytes. The length of the TypeId, Encoding and the Length are fields defined by the ExtensionObject.

Table 26 – Sample OPC UA Binary Encoded Structure with optional fields

Field

Bytes

Value

Type Id

4

The identifier for the TypeA Binary Encoding Node

Encoding

1

0x1 for ByteString

Length

4

13

EncodingMask

4

0x02 for O2

X

4

The value of X

Y

1

The value of Y

O2

4

The value of O2

If a Structure with optional fields is subtyped, the subtypes extend the EncodingMask defined for the parent.

The Value of the DataTypeDefinition Attribute for a DataType Node describing TypeA is:

Name

Type

Description

defaultEncodingId

NodeId

NodeId of the “TypeA_Encoding_DefaultBinary” Node.

baseDataType

NodeId

“i=22” [Structure]

structureType

StructureType

StructureWithOptionalFields_1 [Structure without optional fields]

fields [0]

StructureField

name

String

“X”

description

LocalizedText

Description of X

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [1]

StructureField

name

String

“O1“

description

LocalizedText

Description of O1

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

true

fields [2]

StructureField

name

String

“Y“

description

LocalizedText

Description of Z

dataType

NodeId

“i=2” [SByte]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [3]

StructureField

name

String

“O2“

description

LocalizedText

Description of O2

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

true

Unions are encoded as a switch field preceding one of the possible fields. The encoding for the selected field is determined by the data type for the field.

The switch field is encoded as a UInt32.

The switch field is the index of the available union fields starting with 1. If the switch field is 0 then no field is present. For any value greater than the number of defined union fields the encoders or decoders shall report an error.

A Union with no fields present has the same meaning as a NULL value. A Union with any field present is not a NULL value even if the value of the field itself is NULL.

The following is an example of a union using C/C++ syntax:

struct Type2

{

Int32 A;

Int32 B;

};

struct Type1

{

Byte Selector;

union

{

Int32Field1;

Type2Field2;

}

Value;

};

In the C/C++ example above, the Selector, Field1 and Field2 are semantically coupled to form a union.

An instance of Type1 would be encoded as byte sequence. The length of the byte sequence depends on the selected field.

An instance of Type1 where field Field1 is available would be encoded as 8-byte sequence. If the instance of Type 1 was encoded in an ExtensionObject it would have the encoded form shown in Table 27 and it would have a total length of 17 bytes. The TypeId, Encoding and the Length are fields defined by the ExtensionObject.

Table 27 – Sample OPC UA Binary Encoded Structure

Field

Bytes

Value

Type Id

4

The identifier for Type1

Encoding

1

0x1 for ByteString

Length

4

8

SwitchValue

4

1 for Field1

Field1

4

The value of Field1

The Value of the DataTypeDefinition Attribute for a DataType Node describing Type1 is:

Name

Type

Description

defaultEncodingId

NodeId

NodeId of the “Type1_Encoding_DefaultBinary” Node.

baseDataType

NodeId

“i=22” [Union]

structureType

StructureType

Union_2 [Union]

fields [0]

StructureField

name

String

“Field1”

description

LocalizedText

Description of Field1

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

true

fields [1]

StructureField

name

String

“Field2“

description

LocalizedText

Description of Field2

dataType

NodeId

NodeId of the Type2 DataType Node (e.g. “ns=3; s=MyType2”)

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

true

The Value of the DataTypeDefinition Attribute for a DataType Node describing Type2 is:

Name

Type

Description

defaultEncodingId

NodeId

NodeId of the “Type2_Encoding_DefaultBinary” Node.

baseDataType

NodeId

“i=22” [Structure]

structureType

StructureType

Structure_0 [Structure without optional fields]

fields [0]

StructureField

name

String

“A“

description

LocalizedText

Description of A

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

fields [1]

StructureField

name

String

“B“

description

LocalizedText

Description of B

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

arrayDimensions

UInt32[]

null

maxStringLength

UInt32

0

isOptional

Boolean

false

Messages are Structures encoded as sequence of bytes prefixed by the NodeId of for the OPC UA Binary DataTypeEncoding defined for the Message.

Each OPC UA Service described in OPC 10000-4 has a request and response Message. The DataTypeEncoding IDs assigned to each Service are specified in Clause A.3.

Most built-in types are encoded in XML using the formats defined in XML Schema Part 2 specification. Any special restrictions or usages are discussed below. Some of the built-in types have an XML Schema defined for them using the syntax defined in XML Schema Part 2.

The prefix xs: is used to denote a symbol defined by the XML Schema specification.

A Boolean value is encoded as an xs:boolean value.

Integer values are encoded using one of the subtypes of the xs:decimal type. The mappings between the OPC UA integer types and XML schema data types are shown in Table 28.

Table 28 – XML Data Type Mappings for Integers

Name

XML Type

SByte

xs:byte

Byte

xs:unsignedByte

Int16

xs:short

UInt16

xs:unsignedShort

Int32

xs:int

UInt32

xs:unsignedInt

Int64

xs:long

UInt64

xs:unsignedLong

Floating point values are encoded using one of the XML floating point types. The mappings between the OPC UA floating point types and XML schema data types are shown in Table 29.

Table 29 – XML Data Type Mappings for Floating Points

Name

XML Type

Float

xs:float

Double

xs:double

The XML floating point type supports positive infinity (INF), negative infinity (-INF) and not-a-number (NaN).

A String value is encoded as an xs:string value.

Strings with embedded nulls (‘\u0000’) are not guaranteed to be interoperable because not all DevelopmentPlatforms can handle Strings with embedded nulls. For this reason, embedded nulls are not recommended. Encoders may encode Strings with embedded nulls. Decoders shall read all bytes in String; however, decoders may truncate the String at the first embedded null before passing it on to the application.

A DateTime value is encoded as an xs:dateTime value.

All DateTime values shall be encoded as UTC times or with the time zone explicitly specified.

Correct:

2002-10-10T00:00:00+05:00

2002-10-09T19:00:00Z

Incorrect:

2002-10-09T19:00:00

It is recommended that all xs:dateTime values be represented in UTC format.

The earliest and latest date/time values that can be represented on a DevelopmentPlatform have special meaning and shall not be literally encoded in XML.

The earliest date/time value on a DevelopmentPlatform shall be encoded in XML as '0001-01-01T00:00:00Z'.

The latest date/time value on a DevelopmentPlatform shall be encoded in XML as '9999-12-31T23:59:59Z'

If a decoder encounters a xs:dateTime value that cannot be represented on the DevelopmentPlatform it should convert the value to either the earliest or latest date/time that can be represented on the DevelopmentPlatform. The XML decoder should not generate an error if it encounters an out of range date value.

The earliest date/time value on a DevelopmentPlatform is equivalent to a null date/time value.

A Guid is encoded using the string representation defined in 5.1.3.

The XML schema for a Guid is:

<xs:complexType name="Guid">

<xs:sequence>

<xs:element name="String" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

A ByteString value is encoded as an xs:base64Binary value (see Base64).

The XML schema for a ByteString is:

<xs:element name="ByteString" type="xs:base64Binary" nillable="true"/>

An XmlElement value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="XmlElement">

<xs:sequence>

<xs:any minOccurs="0" maxOccurs="1" processContents="lax" />

</xs:sequence>

</xs:complexType>

XmlElements may only be used inside Variant or ExtensionObject values.

A NodeId value is encoded as an xs:string with the syntax defined in 5.1.12.

The XML schema for a NodeId is:

<xs:complexType name="NodeId">

<xs:sequence>

<xs:element name="Identifier" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

An ExpandedNodeId value is encoded as an xs:string with the syntax defined in 5.1.12.

The XML schema for an ExpandedNodeId is:

<xs:complexType name="ExpandedNodeId">

<xs:sequence>

<xs:element name="Identifier" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

A StatusCode is encoded as an xs:unsignedInt with the following XML schema:

<xs:complexType name="StatusCode">

<xs:sequence>

<xs:element name="Code" type="xs:unsignedInt" minOccurs="0" />

</xs:sequence>

</xs:complexType>

An DiagnosticInfo value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="DiagnosticInfo">

<xs:sequence>

<xs:element name="SymbolicId" type="xs:int" minOccurs="0" />

<xs:element name="NamespaceUri" type="xs:int" minOccurs="0" />

<xs:element name="Locale" type="xs:int" minOccurs="0/>

<xs:element name="LocalizedText" type="xs:int" minOccurs="0/>

<xs:element name="AdditionalInfo" type="xs:string" minOccurs="0"/>

<xs:element name="InnerStatusCode" type="tns:StatusCode"

minOccurs="0" />

<xs:element name="InnerDiagnosticInfo" type="tns:DiagnosticInfo"

minOccurs="0" />

</xs:sequence>

</xs:complexType>

DiagnosticInfo allows unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.

A QualifiedName value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="QualifiedName">

<xs:sequence>

<xs:element name="NamespaceIndex" type="xs:int" minOccurs="0" />

<xs:element name="Name" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

A LocalizedText value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="LocalizedText">

<xs:sequence>

<xs:element name="Locale" type="xs:string" minOccurs="0" />

<xs:element name="Text" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

An ExtensionObject value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="ExtensionObject">

<xs:sequence>

<xs:element name="TypeId" type="tns:NodeId" minOccurs="0" />

<xs:element name="Body" minOccurs="0">

<xs:complexType>

<xs:sequence>

<xs:any minOccurs="0" processContents="lax"/>

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:sequence>

</xs:complexType>

The body of the ExtensionObject contains a single element which is either a ByteString or XML encoded Structure. A decoder can distinguish between the two by inspecting the top-level element. An element with the name tns:ByteString contains an OPC UA Binary encoded body. Any other name shall contain an OPC UA XML encoded body. The TypeId specifies the syntax of a ByteString body which could be UTF-8 encoded JSON, UA Binary or some other format.

The TypeId is the NodeId for the DataTypeEncoding Object.

A Variant value is encoded as an xs:complexType with the following XML schema:

<xs:complexType name="Variant">

<xs:sequence>

<xs:element name="Value" minOccurs="0" nillable="true">

<xs:complexType>

<xs:sequence>

<xs:any minOccurs="0" processContents="lax"/>

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:sequence>

</xs:complexType>

If the Variant represents a scalar value, then it shall contain a single child element with the name of the built-in type. For example, the single precision floating point value 3,141 5 would be encoded as:

<tns:Float>3.1415</tns:Float>

If the Variant represents a single dimensional array, then it shall contain a single child element with the prefix 'ListOf' and the name built-in type. For example, an Array of strings would be encoded as:

<tns:ListOfString>

<tns:String>Hello</tns:String>

<tns:String>World</tns:String>

</tns:ListOfString>

If the Variant represents a multidimensional Array, then it shall contain a child element with the name ‘Matrix’ with the two sub-elements shown in this example:

<tns:Matrix>

<tns:Dimensions>

<tns:Int32>2</tns:Int32>

<tns:Int32>2</tns:Int32>

</tns:Dimensions>

<tns:Elements>

<tns:String>A</tns:String>

<tns:String>B</tns:String>

<tns:String>C</tns:String>

<tns:String>D</tns:String>

</tns:Elements>

</tns:Matrix>

In this example, the array has the following elements:

[0,0] = "A"; [0,1] = "B"; [1,0] = "C"; [1,1] = "D"

The elements of a multi-dimensional Array are always flattened into a single dimensional Array where the higher rank dimensions are serialized first. This single dimensional Array is encoded as a child of the ‘Elements’ element. The ‘Dimensions’ element is an Array of Int32 values that specify the dimensions of the array starting with the lowest rank dimension. The multi-dimensional Array can be reconstructed by using the dimensions encoded. All dimensions shall be specified and shall be greater than zero. If the dimensions are inconsistent with the number of elements in the array, then the decoder shall stop and raise a Bad_DecodingError.

The complete set of built-in type names is found in Table 1.

A DataValue value is encoded as a xs:complexType with the following XML schema:

<xs:complexType name="DataValue">

<xs:sequence>

<xs:element name="Value" type="tns:Variant" minOccurs="0"

nillable="true" />

<xs:element name="StatusCode" type="tns:StatusCode"

minOccurs="0" />

<xs:element name="SourceTimestamp" type="xs:dateTime"

minOccurs="0" />

<xs:element name="SourcePicoseconds" type="xs:unsignedShort"

minOccurs="0"/>

<xs:element name="ServerTimestamp" type="xs:dateTime"

minOccurs="0" />

<xs:element name="ServerPicoseconds" type="xs:unsignedShort"

minOccurs="0"/>

</xs:sequence>

</xs:complexType>

The subtypes of the built-in DataTypes described in 5.3.1 are encoded as the base Built-in DataType.

A Decimal Value is a encoded as an xs:complexType with the following XML schema:

<xs:complexType name="Decimal">

<xs:sequence>

<xs:element name="TypeId" type="tns:NodeId" minOccurs="0" />

<xs:element name="Body" minOccurs="0">

<xs:complexType>

<xs:sequence>

<xs:element name="Scale" type="xs:short" />

<xs:element name="Value" type="xs:string" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:sequence>

</xs:complexType>

The NodeId is always the NodeId of the Decimal DataType. When encoded in a Variant the Decimal is encoded as an ExtensionObject. Arrays of Decimals are Arrays of ExtensionObjects.

The Value is a base-10 signed integer with no limit on size. See 5.1.10 for a description of the Scale and Value fields.

Enumerations that are used as parameters in the Messages defined in OPC 10000-4 are encoded as xs:string with the following syntax:

<symbol>_<value>

The elements of the syntax are described in Table 30.

Table 30 – Components of Enumeration

Field

Type

Description

<symbol>

String

The symbolic name for the enumerated value.

<value>

UInt32

The numeric value associated with enumerated value.

For example, the XML schema for the NodeClass enumeration is:

<xs:simpleType name="NodeClass">

<xs:restriction base="xs:string">

<xs:enumeration value="Unspecified_0" />

<xs:enumeration value="Object_1" />

<xs:enumeration value="Variable_2" />

<xs:enumeration value="Method_4" />

<xs:enumeration value="ObjectType_8" />

<xs:enumeration value="VariableType_16" />

<xs:enumeration value="ReferenceType_32" />

<xs:enumeration value="DataType_64" />

<xs:enumeration value="View_128" />

</xs:restriction>

</xs:simpleType>

Enumerations that are stored in a Variant are encoded as an Int32 value.

For example, any Variable could have a value with a DataType of NodeClass. In this case, the corresponding numeric value is placed in the Variant (e.g. NodeClass Object would be stored as a 1).

One dimensional Array parameters are always encoded by wrapping the elements in a container element and inserting the container into the structure. The name of the container element should be the name of the parameter. The name of the element in the array shall be the type name.

For example, the Read service takes an array of ReadValueIds. The XML schema would look like:

<xs:complexType name="ListOfReadValueId">

<xs:sequence>

<xs:element name="ReadValueId" type="tns:ReadValueId"

minOccurs="0" maxOccurs="unbounded" nillable="true" />

</xs:sequence>

</xs:complexType>

The nillable attribute shall be specified because XML encoders will drop elements in arrays if those elements are empty.

Multi-dimensional Array parameters are encoded using the Matrix type defined in 5.3.1.17.

Structures are encoded as a xs:complexType with all of the fields appearing in a sequence. All fields are encoded as an xs:element. All elements have minOccurs set 0 to allow for compact XML representations. If an element is missing the DefaultValue for the field type is used. If the field type is a structure the DefaultValue is an instance of the structure with DefaultValues for each contained field.

The xs:name attribute restricts the set of characters that are permitted. If a DataType name or its field names uses characters that are not permitted then the DataTypeDefinition (see F.12) or DataTypeField (seeF.13) shall specify a SymbolicName that can be used in a xs:name.

Types which have a NULL value defined shall have the nillable="true" flag set.

For example, the Read service has a ReadValueId structure in the request. The XML schema would look like:

<xs:complexType name="ReadValueId">

<xs:sequence>

<xs:element name="NodeId" type="tns:NodeId"

minOccurs="0" nillable="true" />

<xs:element name="AttributeId" type="xs:int" minOccurs="0" />

<xs:element name="IndexRange" type="xs:string"

minOccurs="0" nillable="true" />

<xs:element name="DataEncoding" type="tns:NodeId"

minOccurs="0" nillable="true" />

</xs:sequence>

</xs:complexType>

Structures with optional fields are encoded as a xs:complexType with all of the fields appearing in a sequence. The first element is a bit mask that specifies what fields are encoded. The bits in the mask are sequentially assigned to optional fields in the order they appear in the Structure.

To allow for compact XML, any field can be omitted from the XML so decoders shall assign DefaultValues based on the field type for any mandatory fields.

The xs:name attribute restricts the set of characters that are permitted. See 5.3.5 for instructions on how to handle invalid characters.

For example, the following Structure has one mandatory and two optional fields. The XML schema would look like:

<xs:complexType name="OptionalType">

<xs:sequence>

<xs:element name="EncodingMask" type="xs:unsignedLong" />

<xs:element name="X" type="xs:int" minOccurs="0" />

<xs:element name="O1" type="xs:int" minOccurs="0" />

<xs:element name="Y" type="xs:byte" minOccurs="0" />

<xs:element name="O2" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

In the example above, the EncodingMask has a value of 3 if both O1 and O2 are encoded. Encoders shall set unused bits to 0 and decoders shall ignore unused bits.

If a Structure with optional fields is subtyped, the subtypes extend the EncodingMask defined for the parent.

Unions are encoded as an xs:complexType containing an xs:sequence with two entries.

The first entry in the sequence is the SwitchField xs:element and specifies a numeric value which identifies which element in the xs:choice is encoded. The name of the element may be any valid text.

The second entry in the sequence is an xs:choice which specifies the possible fields. The order in the xs:choice determines the value of the SwitchField when that choice is encoded. The first element has a SwitchField value of 1 and the last value has a SwitchField equal to the number of choices.

No additional elements in the sequence are permitted. If the SwitchField is missing or 0 then the union has a NULL value. Encoders or decoders shall report an error for any SwitchField value greater than the number of defined union fields.

The name attribute restricts the set of characters that are permitted. See 5.3.5 for instructions on how to handle invalid characters.

For example, the following union has two fields. The XML schema would look like:

<xs:complexType name="Type1">

<xs:sequence>

<xs:element name="SwitchField"

type="xs:unsignedInt" minOccurs="0"/>

<xs:choice>

<xs:element name="Field1" type="xs:int" minOccurs="0"/>

<xs:element name="Field2" type="tns:Field2" minOccurs="0"/> </xs:choice>

</xs:sequence>

</xs:complexType>

Messages are encoded as an xs: complexType. The parameters in each Message are serialized in the same way the fields of a Structure are serialized.

The JSON DataEncoding was developed to allow OPC UA applications to interoperate with web and enterprise software that use this format. The OPC UA JSON DataEncoding defines standard JSON representations for all OPC UA Built-In types.

The JSON format is defined in RFC 8259. It is partially self-describing because each field has a name encoded in addition to the value, however, JSON has no mechanism to qualify names with namespaces.

The JSON format does not have a published standard for a schema that can be used to describe the contents of a JSON document. However, the schema mechanisms defined in this document can be used to describe JSON documents. Specifically, the DataTypeDescription structure defined in OPC 10000-3 can define any JSON document that conforms to the rules described below.

Servers that support the JSON DataEncoding shall add DataTypeEncoding Nodes called “Default JSON” to all DataTypes which can be serialized with the JSON encoding. The NodeIds of these Nodes are defined by the information model which defines the DataType. These NodeIds are used in ExtensionObjects as described in 5.4.2.16.

There are two important use cases for the JSON encoding: Cloud applications which consume PubSub messages and JavaScript Clients (JSON is the preferred serialization format for JavaScript). For the Cloud application use case, the PubSub message needs to be self-contained which implies it cannot contain numeric references to an externally defined namespace table. Cloud applications also often rely on scripting languages to process the incoming messages so artefacts in the DataEncoding that exist to ensure fidelity during decoding are not necessary. For this reason, this DataEncoding defines a ‘non-reversible’ form which is designed to meet the needs of Cloud applications. Applications, such as JavaScript Clients, which use the DataEncoding for communication with other OPC UA applications use the normal or ‘reversible’ form. The differences, if any, between the reversible and non-reversible forms are described for each type.

Any value for a Built-In type that is NULL shall be encoded as the JSON literal ‘null’ if the value is an element of an array. If the NULL value is for a field within a Structure or Union, the field shall not be encoded.

Note that JSON objects are unordered sets of name-value pairs. When a Built-In type is encoded as a JSON object, the order of fields specified is not preserved.

A Boolean value shall be encoded as the JSON literal ‘true’ or ‘false’.

Integer values other than Int64 and UInt64 shall be encoded as a JSON number.

Int64 and UInt64 values shall be formatted as a decimal number encoded as a JSON string

(See the XML encoding of 64-bit values described in 5.3.1.3).

Normal Float and Double values shall be encoded as a JSON number.

Special floating-point numbers such as positive infinity (INF), negative infinity (-INF) and not-a-number (NaN) shall be represented by the values “Infinity”, “-Infinity” and “NaN” encoded as a JSON string. See 5.2.2.3 for more information on the different types of special floating-point numbers.

String values shall be encoded as JSON strings.

Any characters which are not allowed in JSON strings are escaped using the rules defined in RFC 8259.

Strings with embedded nulls (‘\u0000’) are not guaranteed to be interoperable because not all DevelopmentPlatforms can handle Strings with embedded nulls. For this reason, embedded nulls are not recommended. Encoders may encode Strings with embedded nulls. Decoders shall read all bytes in String; however, decoders may truncate the String at the first embedded null before passing it on to the application.

DateTime values shall be formatted as specified by ISO 8601-1 and encoded as a JSON string.

DateTime values which exceed the minimum or maximum values supported on a platform shall be encoded as “0001-01-01T00:00:00Z” or “9999-12-31T23:59:59Z” respectively. During decoding, these values shall be converted to the minimum or maximum values supported on the platform.

ISO 8601-1 DateType values may specify an arbitrary number of decimal places representing fractions of seconds. Encoders shall support as many decimal places needed to represent the full range of the DateTime type on their DevelopmentPlatform. Decoders may truncate decimal places that exceed the range supported by the DateTime type on their DevelopmentPlatform.

DateTime values equal to “0001-01-01T00:00:00Z” are considered to be NULL values.

Guid values shall be formatted as described in 5.1.3 and encoded as a JSON string.

ByteString values shall be formatted as a Base64 text and encoded as a JSON string.

Any characters which are not allowed in JSON strings are escaped using the rules defined in RFC 8259.

XmlElement value shall be encoded as a String as described in 5.4.2.5.

NodeId values shall be encoded as a JSON object with the fields defined in Table 31.

The abstract NodeId structure is defined in OPC 10000-3 and has three fields Id, IdType and NamespaceIndex. The representation of these abstract fields are described in the table.

Table 31 – JSON Object Definition for a NodeId

Name

Description

IdType

The IdentifierType encoded as a JSON number.

Allowed values are:

0 - UInt32 identifier encoded as a JSON number.

1 - A String identifier encoded as a JSON string.

2 - A Guid identifier encoded as described in 5.4.2.7.

3 - A ByteString identifier encoded as described in 5.4.2.8.

This field is omitted for UInt32 identifiers.

Id

The identifier.

The value of the IdType field specifies the encoding of this field.

Namespace

For reversible encoding this field is a JSON number with the NamespaceIndex. The field is omitted if the NamespaceIndex is 0.

For non-reversible encoding this field is the JSON string containing the NamespaceUri associated with the NamespaceIndex unless the NamespaceIndex is 0. If the NamespaceIndex is 0 the field is omitted.

ExpandedNodeId values shall be encoded as a JSON object with the fields defined in Table 32.

The abstract ExpandedNodeId structure is defined in OPC 10000-4 and has five fields Identifier, IdentifierType, NamespaceIndex, NamespaceUri and ServerIndex. The representation of these abstract fields are described in the table.

Table 32 – JSON Object Definition for an ExpandedNodeId

Name

Description

IdType

The IdentifierType encoded as a JSON number.

Allowed values are:

0 - UInt32 Identifier encoded as a JSON number.

1 - A String Identifier encoded as a JSON string.

2 - A Guid Identifier encoded as described in 5.4.2.7.

3 - A ByteString Identifier encoded as described in 5.4.2.8.

This field is omitted for UInt32 identifiers.

Id

The Identifier.

The value of the IdType field specifies the encoding of this field.

Namespace

For reversible encoding this field is a JSON string with the NamespaceUri if the NamespaceUri is specified. Otherwise, it is a JSON number with the NamespaceIndex. The field is omitted if the NamespaceIndex is 0.

For non-reversible encoding this field is the JSON string containing the NamespaceUri or the NamespaceUri associated with the NamespaceIndex unless the NamespaceIndex is 0 or 1. If the NamespaceIndex is 0 the field is omitted.

ServerUri

For reversible encoding this field is a JSON number with the ServerIndex. The field is omitted if the ServerIndex is 0.

For non-reversible encoding this field is the JSON string containing the ServerUri associated with the ServerIndex unless the ServerIndex is 0. If the ServerIndex is 0 the field is omitted.

StatusCode values shall be encoded as a JSON number for the reversible encoding. If the StatusCode is Good (0) it is only encoded if it is an element of a JSON array.

For the non-reversible form, StatusCode values shall be encoded as a JSON object with the fields defined in Table 33.

Table 33 – JSON Object Definition for a StatusCode

Name

Description

Code

The numeric code encoded as a JSON number.

The Code is omitted if the numeric code is 0 (Good).

Symbol

The string literal associated with the numeric code encoded as JSON string.

e.g. 0x80AB0000 has the associated literal “Bad_InvalidArgument”.

The Symbol is omitted if the numeric code is 0 (Good).

If the string literal is not known to the encoder the field is omitted.

For the non-reversible encoding, a StatusCode of Good (0) it is only encoded if it is an element JSON array.

DiagnosticInfo values shall be encoded as a JSON object with the fields shown in Table 34.

Table 34 – JSON Object Definition for a DiagnosticInfo

Name

Data Type

Description

SymbolicId

Int32

A symbolic name for the status code.

The default value is -1.

NamespaceUri

Int32

A namespace that qualifies the symbolic id.

The default value is -1.

Locale

Int32

The locale used for the localized text.

The default value is -1.

LocalizedText

Int32

A human readable summary of the status code.

The default value is -1.

AdditionalInfo

String

Detailed application specific diagnostic information.

The default value is null.

InnerStatusCode

StatusCode

A status code provided by an underlying system.

The default value is Good.

InnerDiagnosticInfo

DiagnosticInfo

Diagnostic info associated with the inner status code.

The default value is null.

Each field is encoded using the rules defined for the built-in type specified in the Data Type column. Fields should be omitted if the value is null or the default value for the field. If a field is not present the value shall be interpreted as the default value.

The SymbolicId, NamespaceUri, Locale and LocalizedText fields are encoded as JSON numbers which reference the StringTable contained in the ResponseHeader.

QualifiedName values shall be encoded as a JSON object with the fields shown in Table 35.

The abstract QualifiedName structure is defined in OPC 10000-3 and has two fields Name and NamespaceIndex. The NamespaceIndex is represented by the Uri field in the JSON object.

Table 35 – JSON Object Definition for a QualifiedName

Name

Description

Name

The Name component of the QualifiedName.

Uri

For reversible encoding this field is a JSON number with the NamespaceIndex. The field is omitted if the NamespaceIndex is 0.

For non-reversible encoding this field is the JSON string containing the NamespaceUri associated with the NamespaceIndex unless the NamespaceIndex is 0. If the NamespaceIndex is 0 the field is omitted.

LocalizedText values shall be encoded as a JSON object with the fields shown in Table 36.

The abstract LocalizedText structure is defined in OPC 10000-3 and has two fields Text and Locale.

Table 36 – JSON Object Definition for a LocalizedText

Name

Description

Locale

The Locale portion of LocalizedText values shall be encoded as a JSON string

Text

The Text portion of LocalizedText values shall be encoded as a JSON string.

For the non-reversible form, LocalizedText value shall be encoded as a JSON string containing the Text component.

ExtensionObject values shall be encoded as a JSON object with the fields shown in Table 37.

Table 37 – JSON Object Definition for an ExtensionObject

Name

Description

TypeId

The NodeId of a DataTypeEncoding Node or a DataType Node formatted using the rules in 5.4.2.10.

Encoding

The format of the Body field encoded as a JSON number.

This value is 0 if the body is Structure encoded as a JSON object (see 5.4.6).

This value is 1 if the body is a ByteString value encoded as a JSON string (see 5.4.2.8).

This value is 2 if the body is a XmlElement value encoded as a JSON string (see 5.4.2.9).

This field is omitted if the value is 0.

Body

Body of the ExtensionObject. The type of this field is specified by the Encoding field.

If the Body is empty, the ExtensionObject is NULL and is omitted or encoded as a JSON null.

For the non-reversible form, ExtensionObject values shall be encoded as a JSON value containing only the value of the Body field. The TypeId and Encoding fields are dropped.

Variant values shall be encoded as a JSON object with the fields shown in Table 38.

Table 38 – JSON Object Definition for a Variant

Name

Description

Type

The Built-in type for the value contained in the Body (see Table 1) encoded as JSON number.

If type is 0 (NULL) the Variant contains a NULL value and the containing JSON object shall be omitted or replaced by the JSON literal ‘null’ (when an element of a JSON array).

Body

If the value is a scalar, it is encoded using the rules for type specified for the Type.

If the value is a one-dimensional array it is encoded as JSON array (see 5.4.5).

Multi-dimensional arrays are encoded as a one-dimensional JSON array which is reconstructed using the value of the Dimensions field (see 5.2.2.16).

Dimensions

The dimensions of the array encoded as a JSON array of JSON numbers.

The Dimensions are omitted for scalar and one-dimensional array values.

For the non-reversible form, Variant values shall be encoded as a JSON value containing only the value of the Body field. The Type and Dimensions fields are dropped. Multi-dimensional arrays are encoded as a multi-dimensional JSON array as described in 5.4.5.

DataValue values shall be encoded as a JSON object with the fields shown in Table 39.

Table 39 – JSON Object Definition for a DataValue

Name

Data Type

Description

Value

Variant

The value.

Status

StatusCode

The status associated with the value.

SourceTimestamp

DateTime

The source timestamp associated with the value.

SourcePicoseconds

UInt16

The number of 10 Picosecond intervals for the SourceTimestamp.

ServerTimestamp

DateTime

The Server timestamp associated with the value.

ServerPicoseconds

UInt16

The number of 10 Picosecond intervals for the ServerTimestamp.

If a field has a null or DefaultValue it is omitted. Each field is encoded using the rules defined for the built-in type specified in the Data Type column.

Decimal values shall be encoded as a JSON object with the fields in Table 40.

Table 40 – JSON Object Definition for a Decimal

Name

Description

Scale

A JSON number with the scale applied to the Value.

Value

A JSON string with the Value encoded as a base-10 signed integer.

(See the XML encoding of Integer values described in 5.3.1.3).

See 5.1.10 for a description of the Scale and Value fields.

When encoding in a Variant, a Decimal value shall be encoded as an ExtensionObject with the JSON object in Table 40 as the Body. The TypeId shall be the NodeId of the Decimal DataType and the Encoding shall be 0.

Enumeration values shall be encoded as a JSON number for the reversible encoding.

For the non-reversible form, Enumeration values are encoded as a JSON string with the following format:

<name>_<value>

Where the name is the enumeration literal and the value is the numeric value.

If the literal is not known to the encoder, the numeric value is encoded as a JSON string.

If an Enumeration is encoded using the reversible encoding within a Variant, the Type field is Built-in Type Int32.

One dimensional Arrays shall be encoded as JSON arrays.

If an element is NULL, the element shall be encoded as the JSON literal ‘null’.

Otherwise, the element is encoded according to the rules defined for the type.

Multi-dimensional Arrays are encoded as nested JSON arrays. The outer array is the first dimension and the innermost array is the last dimension. For example, the following matrix

0 2 3

1 3 4

is encoded in JSON as

[ [0, 2, 3], [1, 3, 4] ]

Structures shall be encoded as JSON objects.

Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Structure is serialized in JSON.

Fields which are NULL or have a default value shall be encoded using the rules shown in Table 41.

Table 41 – JSON Encoding Rules for Structures

Field Value

Reversible

Non-Reversible

NULL

Omitted

JSON null

Default Value

Omitted

Default Value

For example, instances of the structures:

struct Type2

{

Int32 A;

Int32 B;

Char* C;

};

struct Type1

{

Int32 X;

Int32 NoOfY;

Type2* Y;

Int32 Z;

};

The reversible encoding is represented in JSON as:

{

"X":1234,

"Y":[ { "A":1, "B":2, "C":"Hello" }, { "A":3, "B":4 } ],

"Z":5678

}

Where “C” is omitted from the second Type2 instance because it has a NULL value.

The non-reversible encoding is represented in JSON as:

{

"X":1234,

"Y":[ { "A":1, "B":2, "C":"Hello" }, { "A":3, "B":4, "C":null } ],

"Z":5678

}

Where “C” in the second Type2 instance has a JSON null value.

Structures with optional fields shall be encoded as JSON objects as shown in Table 42.

Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Structure is serialized in JSON. The EncodingMask may not appear as the first field.

Table 42 – JSON Object Definition for a Structures with Optional Fields

Name

Description

EncodingMask

A bit mask indicating what fields are encoded in the structure (see 5.2.7)

This mask is encoded as a JSON number.

The bits are sequentially assigned to optional fields in the order that they are defined.

This field is not encoded in the non-reversible form.

<FieldName>

The field in structure encoded according to the rules defined for their DataType.

One entry may exist for each mandatory field and each optional field that is present.

Fields which are NULL or have a default value shall be encoded using the rules shown in Table 43.

Table 43 – JSON Encoding Rules for Structures with Optional Fields

Field Value

Field Type

Reversible

Non-Reversible

NULL

Mandatory

Omitted

JSON null

Default Value

Mandatory

Omitted

Default Value

NULL

Optional (Present)

Omitted

JSON null

Default Value

Optional (Present)

Omitted

Default Value

NULL

Optional (Omitted)

Omitted

Omitted

Default Value

Optional (Omitted)

Omitted

Omitted

If a Structure with optional fields is subtyped, the subtypes extend the EncodingMask defined for the parent.

The following is an example of a structure with optional fields using C++ syntax:

struct TypeA

{

Int32 X;

Int32* O1;

SByte Y;

Int32* O2;

};

O1 and O2 are optional fields where a NULL indicates that the field is not present.

Assume that O1 is not specified and the value of O2 is 0.

The reversible encoding would be:

{ "EncodingMask": 2 "X": 1, "Y": 2 }

Where decoders would assign the default value of 0 to O2 since the mask bit is set even though the field was omitted (this is the behaviour defined for the Int32 DataType). Decoders would mark O1 as ‘not specified’.

The non-reversible encoding would be:

{ "X": 1, "Y": 2, "O2": 0 }

Where the EncodingMask is omitted.

Unions shall be encoded as JSON objects as shown in Table 44 for the reversible encoding.

Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Union is serialized in JSON. The SwitchField may not appear as the first field.

Table 44 – JSON Object Definition for a Union

Name

Description

SwitchField

The identifier for the field in the Union which is encoded as a JSON number.

The valid values for this field follow the conventions defined in 5.2.8.

If the SwitchField value is its DefaultValue of 0, then the SwitchField and the Value field are not present.

Value

The value of the field encoded using the rules that apply to the data type.

For the non-reversible form, Union values are encoded using the rule for the current value. If the SwitchField is 0 the Union is encoded as a JSON null value.

For example, instances of the union:

struct Union1

{

Byte Selector;

{

Int32 A;

Double B;

Char* C;

}

Value;

};

would be represented in reversible form as:

{ "SwitchField":2, "Value":3.1415 }

In non-reversible form, it is represented as:

3.1415

Messages are encoded ExtensionObjects (see 5.4.2.16).