This standard defines three data encodings: 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

Description

1

Boolean

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

2

SByte

An integer value between −128 and 127 inclusive.

3

Byte

An integer value between 0 and 255 inclusive.

4

Int16

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

5

UInt16

An integer value between 0 and 65 535 inclusive.

6

Int32

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

7

UInt32

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

8

Int64

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

9

UInt64

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

10

Float

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

11

Double

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

12

String

A sequence of Unicode characters.

13

DateTime

An instance in time.

14

Guid

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

15

ByteString

A sequence of octets.

16

XmlElement

An XML element.

17

NodeId

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

18

ExpandedNodeId

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

19

StatusCode

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

20

QualifiedName

A name qualified by a namespace.

21

LocalizedText

Human readable text with an optional locale identifier.

22

ExtensionObject

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

23

DataValue

A data value with an associated status code and timestamps.

24

Variant

A union of all of the types specified above.

25

DiagnosticInfo

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 standard. In addition, this standard defines a representation for the Guid type defined in OPC 10000-3.

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

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.

An ExtensionObject is a container for any Structured DataTypes which cannot be encoded as one of the other built-in data types. The ExtensionObject contains a complex value serialized as a sequence of bytes or as an XML element. It also contains an identifier which indicates what data it contains and how it is encoded.

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.

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.

Variables with a DataType of BaseDataType are mapped to a Variant, however, the ValueRank and ArrayDimensions Attributes place restrictions on what is allowed in the Variant. For example, if the ValueRank is Scalar then the Variant may only contain scalar values.

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 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 Decimal.If the length is less than or equal to 0 then the Decimal value is 0.

Scale

Int16

A signed integer representing the power of ten used to scale the value.

i.e. the decimal number of the value * 10-scale

The integer is encoded starting with the least significant bit.

Value

Byte [*]

A 2-complement signed integer representing the unscaled value.

The number of bits is inferred from the length of the length field.

If the number of bits is 0 then the value is 0.

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.

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 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 default value 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) should be 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 4 lists the bit ranges for the supported floating point types.

Table 4 – 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) should be 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 (000000000000F8FF) or (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 without a null terminator and 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.

Figure 4 illustrates how the multilingual string “水Boy” should be 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 Clause 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” should be 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 fragment serialized as UTF-8 string and then encoded as ByteString.

Figure 6 illustrates how the XmlElement “<A>Hot水</A>” should be 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 5.

Table 5 – 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

*

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 6. The tables that follow describe the structure of each possible format (they exclude the byte which indicates the format).

Table 6 – 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 7. The standard DataEncoding is used for all formats that do not have an explicit format defined.

Table 7 – 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 8.

Table 8 – 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 9.

Table 9 – 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 10.

Table 10 – 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 11 – 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 12.

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

Table 12 – 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 13 – 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 and the number of bytes encoded.

An ExtensionObject may be encoded by the application which means it is passed as a ByteString or an XmlElement 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 14.

Table 14 – 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 a XmlElement.

Length

Int32

The length of the object body.

The length shall be specified if the body is encoded.

Body

Byte [*]

The object body.

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.

ExtensionObjects are used in two contexts: as values contained in Variant structures or as parameters in OPC UA Messages.

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 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 15.

Table 15 – 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 Array Dimensions 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 and pass both onto the application. Encoders shall not use these IDs.

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]

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 16.

Table 16 – 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 Source Timestamp is DateTime.MinValue.

0x08

False if the Server Timestamp is DateTime.MinValue.

0x10

False if the Source Picoseconds is 0.

0x20

False if the Server Picoseconds is 0.

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.7.

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. If an Array is null, then its length is encoded as −1. An Array of zero length is different from an Array that is null so encoders and decoders shall preserve this distinction.

Multi-dimensional Arrays are encoded as an Int32 Array containing the dimensions followed by a list of all the values in the Array. The total number of values is equal to the product of the dimensions. The number of values is 0 if one or more dimension is less than or equal to 0. The process for reconstructing the multi-dimensional array is described in 5.2.2.16.

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 default values 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;

};

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.

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 17 which would make the total length 37 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 17 – Sample OPC UA Binary Encoded structure

Field

Bytes

Value

Type Id

4

The identifier for Type1

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’

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” [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)

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)

isOptional

Boolean

false

fields [2]

StructureField

name

String

“Z“

description

LocalizedText

Description of Z

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

isOptional

Boolean

false

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)

isOptional

Boolean

false

fields [1]

StructureField

name

String

“B“

description

LocalizedText

Description of B

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

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 is depending 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 17 and have a total length of 20 bytes. The length of the TypeId, Encoding and the Length are fields defined by the ExtensionObject.

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

Field

Bytes

Value

Type Id

4

The identifier for TypeA

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 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)

isOptional

Boolean

false

fields [1]

StructureField

name

String

“O1“

description

LocalizedText

Description of O1

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

isOptional

Boolean

true

fields [2]

StructureField

name

String

“Y“

description

LocalizedText

Description of Z

dataType

NodeId

“i=2” [SByte]

valueRank

Int32

-1 (Scalar)

isOptional

Boolean

false

fields [3]

StructureField

name

String

“O2“

description

LocalizedText

Description of O2

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

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 and Value are semantically coupled to form a union. The order of the fields does not matter.

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 18 and it would have a total length of 15 bytes. The TypeId, Encoding and the Length are fields defined by the ExtensionObject.

Table 18 – 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)

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)

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)

isOptional

Boolean

false

fields [1]

StructureField

name

String

“B“

description

LocalizedText

Description of B

dataType

NodeId

“i=6” [Int32]

valueRank

Int32

-1 (Scalar)

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 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 18.

Table 18 – 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 19.

Table 19 – 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.

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:

ns=<namespaceindex>;<type>=<value>

The elements of the syntax are described in Table 20.

Table 20 – Components of NodeId

Field

Data Type

Description

<namespaceindex>

UInt16

The NamespaceIndex formatted as a base 10 number.

If the index is 0 then the entire 'ns=0;' clause shall be omitted.

<type>

Enumeration

A flag that specifies the IdentifierType.

The flag has the following values:

i

NUMERIC (UInt32)

s

STRING (String)

g

GUID (Guid)

b

OPAQUE (ByteString)

<value>

*

The Identifier encoded as string.

The Identifier is formatted using the XML data type mapping for the IdentifierType.

Note that the Identifier may contain any non-null UTF-8 character including whitespace.

Examples of NodeIds:

i=13

ns=10;i=-1

ns=10;s=Hello:World

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

ns=1;b=M/RbKBsRVkePCePcx24oRA==

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:

svr=<serverindex>;ns=<namespaceindex>;<type>=<value>

or

svr=<serverindex>;nsu=<uri>;<type>=<value>

The possible fields are shown in Table 21.

Table 21 – Components of ExpandedNodeId

Field

Data Type

Description

<serverindex>

UInt32

The ServerIndex formatted as a base 10 number.

If the ServerIndex is 0 then the entire 'svr=0;' clause shall be omitted.

<namespaceindex>

UInt16

The NamespaceIndex formatted as a base 10 number.

If the NamespaceIndex is 0 then the entire 'ns=0;' clause shall be omitted.

The NamespaceIndex shall not be present if the URI is present.

<uri>

String

The NamespaceUri formatted as a string.

Any reserved characters in the URI shall be replaced with a ‘%’ followed by its 8 bit ANSI value encoded as two hexadecimal digits (case insensitive). For example, the character ‘;’ would be replaced by ‘%3B’.

The reserved characters are ‘;’ and ‘%’.

If the NamespaceUri is null or empty, then 'nsu=;' clause shall be omitted.

<type>

Enumeration

A flag that specifies the IdentifierType.

This field is described in Table 20.

<value>

*

The Identifier encoded as string.

This field is described in Table 20.

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>

A Decimal Value is a encoded as an xs:complexType wit 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:unsignedShort" />

<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.7 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 22.

Table 22 – 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 default value for the field type is used. If the field type is a structure the default value is an instance of the structure with default values for each contained field.

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 default values based on the field type for any mandatory fields.

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 the parent.

Unions are encoded as a xs:complexType containing a 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 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.

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 7159. 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 specification 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’ from. 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 a field within a Structure or Union, the field shall not be encoded.

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 7159.

DateTime values shall be formatted as specified by ISO 8601:2004 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.

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 7159.

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 23.

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

Table 23 – 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 id field specifies the encoding of this field.

Namespace

The NamespaceIndex for the NodeId.

The field is encoded as a JSON number for the reversible encoding.

The field is omitted if the NamespaceIndex equals 0.

For the non-reversible encoding, the field is the NamespaceUri associated with the NamespaceIndex, encoded as a JSON string.

A NamespaceIndex of 1 is always encoded as a JSON number.

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

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

Table 24 – 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 ‘t’ field specifies the encoding of this field.

Namespace

The NamespaceIndex or the NamespaceUri for the ExpandedNodeId.

If the NamespaceUri is not specified, the NamespaceIndex is encoded with these rules:

The field is encoded as a JSON number for the reversible encoding.

The field is omitted if the NamespaceIndex equals 0.

For the non-reversible encoding the field is the NamespaceUri associated with the NamespaceIndex encoded as a JSON string.

A NamespaceIndex of 1 is always encoded as a JSON number.

If the NamespaceUri is specified it is encoded as a JSON string in this field.

ServerUri

The ServerIndex for the ExpandedNodeId.

This field is encoded as a JSON number for the reversible encoding.

This field is omitted if the ServerIndex equals 0.

For the non-reversible encoding, this field is the ServerUri associated with the ServerIndex portion of the ExpandedNodeId, encoded as a JSON string.

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

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

Table 25 – 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 “BadInvalidArgument”.

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

A StatusCode of Good (0) is treated like a NULL and not encoded. If it is an element of an JSON array it is encoded as the JSON literal ‘null’.

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

Table 26 – JSON Object Definition for a DiagnosticInfo

Name

Data Type

Description

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.

AdditionalInfo

String

Detailed application specific diagnostic information.

InnerStatusCode

StatusCode

A status code provided by an underlying system.

InnerDiagnosticInfo

DiagnosticInfo

Diagnostic info associated with the inner status code.

Each field is encoded using the rules defined for the built-in type specfied in the Data Type column.

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 27.

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 27 – JSON Object Definition for a QualifiedName

Name

Description

Name

The Name component of the QualifiedName.

Uri

The NamespaceIndex component of the QualifiedName encoded as a JSON number.

The Uri field is omitted if the NamespaceIndex equals 0.

For the non-reversible form, the NamespaceUri associated with the NamespaceIndex portion of the QualifiedName is encoded as JSON string unless the NamespaceIndex is 1 or if NamespaceUri is unknown. In these cases, the NamespaceIndex is encoded as a JSON number.

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

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

Table 28 – 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 29.

Table 29 – JSON Object Definition for a ExtensionObject

Name

Description

TypeId

The NodeId of a DataTypeEncoding 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 object 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 30.

Table 30 – 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 an 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 object 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 31.

Table 31 – 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 default value it is omitted. Each field is encoded using the rules defined for the built-in type specfied in the Data Type column.

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

Table 32 – 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.7 for a description of the Scale and Value fields.

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

For the non-reversible form, Enumeration values are encoded the literal with the value appended as a JSON string.

The format of the string literal is:

<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.

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.

If the value of a field is NULL it shall be omitted from the encoding.

For example, instances of the structures:

struct Type2

{

Int32 A;

Int32 B;

Char* C;

};

struct Type1

{

Int32 X;

Int32 NoOfY;

Type2* Y;

Int32 Z;

};

Are 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.

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

Table 33 – 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.

<FieldName>

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

For the non-reversible form, Structures with optional fields are encoded like Structures.

If a Structure with optional fields is subtyped, the subtypes extend the EncodingMask (e) defined for the 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, however, the field was omitted (this is the behaviour defined for the Int32 DataType). Decoders would mark O1 as ‘not specified’.

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

Table 34 – JSON Object Definition for a Union

Name

Description

SwitchField

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

Value

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

For the non-reversible form, Unions values are encoded using the rule for the current 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).