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