Editors Note: The JSON encoding was changed very late in the specification release cycle with limited implementation to validate the design. Subsequent implementations and interoperability testing may result in changes to the JSON encoding in future versions of this specification.
The JSON DataEncoding was developed to allow OPC UA applications to interoperate with web and enterprise software that use this format. The OPC UA JSON DataEncoding defines standard JSON representations for all OPC UA Built-In types.
The JSON format is defined in RFC 8259. It is partially self-describing because each field has a name encoded in addition to the value, however, JSON has no mechanism to qualify names with namespaces.
The JSON format does not have a published standard for a schema that can be used to describe the contents of a JSON document. However, the schema mechanisms defined in this document can be used to describe JSON documents. Specifically, the DataTypeDescription structure defined in OPC 10000-3 can define any JSON document that conforms to the rules described below.
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 identifies some the artefacts which can be removed to meet the needs of cloud applications. Applications, such as JavaScript Clients, which use the DataEncoding for communication with other OPC UA applications require the artefacts.
The CompactEncoding omits all fields with a value equal to the default value for the type. The VerboseEncoding includes all fields.
The CompactEncoding and VerboseEncoding replace the ReversibleEncoding and NonReversibleEncoding. The differences are described in Annex H. The VerboseEncoding also supports the RawData mode defined in OPC 10000-14. In RawData mode, encoders shall omit the following fields:
UaType (see 5.4.2.17 and 5.4.2.18);
UaTypeId (see 5.4.2.16);
Decoders may not be able to process streams encoding in RawData mode unless they have access to the associated metadata. These fields are not omitted when serialization uses abstract DataTypes such as Structure (i.e. ExtensionObject) or BaseDataType (i.e. Variant). OPC 10000-14 specifies the behaviour if a Publisher is misconfigured with metadata that uses abstract DataTypes.
Any value for a nullable Built-In type that is NULL shall be encoded as the JSON literal ‘null’ if the value is an element of a JSON array. If the NULL value is for a field within a JSON object, the field shall be omitted when using the CompactEncoding. When using the VerboseEncoding, the field is encoded as the JSON literal ‘null’.
Any non-nullable Built-In type shall be encoded if it is an element of an array. When using the CompactEncoding, the field of a Structure or Union is omitted if value is equal to the default value for the Built-In type.
Note that JSON objects are unordered sets of name-value pairs. When a Built-In type is encoded as a JSON object, the order of fields specified is not preserved.
A Boolean value shall be encoded as the JSON literal ‘true’ or ‘false’.
Integer values other than Int64 and UInt64 shall be encoded as a JSON number.
Int64 and UInt64 values shall be formatted as a decimal number encoded as a JSON string
(See the XML encoding of 64-bit values described in 5.3.1.3).
Normal Float and Double values shall be encoded as a JSON number.
Special floating-point numbers such as positive infinity (INF), negative infinity (-INF) and not-a-number (NaN) shall be represented by the values “Infinity”, “-Infinity” and “NaN” encoded as a JSON string. See 5.2.2.3 for more information on the different types of special floating-point numbers.
String values shall be encoded as JSON strings.
Any characters which are not allowed in JSON strings are escaped using the rules defined in RFC 8259.
Strings with embedded nulls (‘\u0000’) are not guaranteed to be interoperable because not all DevelopmentPlatforms can handle Strings with embedded nulls. For this reason, embedded nulls are not recommended. Encoders may encode Strings with embedded nulls. Decoders shall read all bytes in String; however, decoders may truncate the String at the first embedded null before passing it on to the application.
DateTime values shall be formatted as specified by ISO 8601-1 and encoded as a JSON string.
DateTime values which exceed the minimum or maximum values supported on a platform shall be encoded as “0001-01-01T00:00:00Z” or “9999-12-31T23:59:59Z” respectively. During decoding, these values shall be converted to the minimum or maximum values supported on the platform.
ISO 8601-1 DateType values may specify an arbitrary number of decimal places representing fractions of seconds. Encoders shall support as many decimal places needed to represent the full range of the DateTime type on their DevelopmentPlatform. Decoders may truncate decimal places that exceed the range supported by the DateTime type on their DevelopmentPlatform.
DateTime values equal to “0001-01-01T00:00:00Z” are considered to be NULL values.
Guid values shall be formatted as described in 5.1.3 and encoded as a JSON string.
ByteString values shall be formatted as a Base64 text and encoded as a JSON string.
Any characters which are not allowed in JSON strings are escaped using the rules defined in RFC 8259.
XmlElement value shall be encoded as a String as described in 5.4.2.5.
NodeId values shall be encoded as a JSON string using the format defined in 5.1.12. NodeIds in NamespaceIndex 0 use the <identifier> form. All other NodeIds use the <namespace-uri> form.
The first abnormal state occurs when the encoder cannot map a NamespaceIndex to a NamespaceUri. In this case, the encoder shall encode the NamespaceIndex using the <namespace-index> form. The decoder shall pass this NamespaceIndex to the application.
The second abnormal state occurs when the decoder cannot convert a NamespaceUri to a NamespaceIndex. If this occurs the decoder shall set the NamespaceIndex to 0, the IdType to String and the Identifier to the JSON string.
ExpandedNodeId values shall be encoded as a JSON string using the format defined in 5.1.12. The NodeId portion of the ExpandedNodeId uses the rules from 5.4.2.10.ExpandedNodeIds with a ServerIndex of 0 are encoded using the <node-id> form. All other ExpandedNodeIds use the <server-uri> form unless the first abnormal state occurs as described below.
The first abnormal state occurs when the encoder cannot map a ServerIndex to a ServerUri. In this case, the encoder shall encode the ServerIndex using the <server-index> form. The decoder shall pass this ServerIndex to the application.
The second abnormal state occurs when the decoder cannot convert a ServerUri to a ServerIndex. If this occurs the decoder shall set the ServerIndex to 0, the NamespaceIndex to 0, the IdType to String and the Identifier to the JSON string.
When the ServerIndex is 0, decoders shall replace a NamespaceUri with a NamespaceIndex when a mapping exists or if a mapping can be created. If no mapping exists or one cannot be created the NamespaceUri is stored in the NamespaceUri field of the ExpandedNodeId.
StatusCode values shall be encoded as a JSON object with the fields defined in Table 36.
Table 36 – 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”. Any InfoBits in the StatusCode are ignored when looking up the symbol. If the string literal is not known to the encoder the field is omitted. The field is omitted in the CompactEncoding. The field is omitted if the numeric code is 0 (Good). The recommended string literals are defined in A.2. |
DiagnosticInfo values shall be encoded as a JSON object with the fields shown in Table 37.
Table 37 – JSON Object Definition for a DiagnosticInfo
Name |
Data Type |
Description |
SymbolicId |
Int32 |
A symbolic name for the status code. The default value is -1. It is not encoded if the value is -1. |
NamespaceUri |
Int32 |
A namespace that qualifies the symbolic id. The default value is -1. It is not encoded if the value is -1. |
Locale |
Int32 |
The locale used for the localized text. The default value is -1. It is not encoded if the value is -1. |
LocalizedText |
Int32 |
A human readable summary of the status code. The default value is -1. It is not encoded if the value is -1. |
AdditionalInfo |
String |
Detailed application specific diagnostic information. The default value is null. It is not encoded if the value is null. |
InnerStatusCode |
StatusCode |
A status code provided by an underlying system. The default value is Good. It is not encoded if the value is Good. |
InnerDiagnosticInfo |
DiagnosticInfo |
Diagnostic info associated with the inner status code. The default value is null. It is not encoded if the value is null. |
The SymbolicId, NamespaceUri, Locale and LocalizedText fields are encoded as JSON numbers which reference the StringTable contained in the ResponseHeader.
DiagnosticInfo is recursive and unlimited recursion could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 4 recursion levels and are not expected to support more than 10. Decoders shall report an error if the number of recursion levels exceeds what it supports.
QualifiedName values shall be encoded as a JSON string using the format defined in 5.1.12 however, the form with the NamespaceIndex is not allowed.
When decoders need to replace a NamespaceUri with a NamespaceIndex and the NamespaceUri cannot be mapped to a NamespaceIndex, then decoders shall use 0 for the NamespaceIndex and the JSON string as the Name.
LocalizedText values shall be encoded as a JSON object with the fields shown in Table 38.
Table 38 – JSON Object Definition for a LocalizedText
Name |
Description |
Locale |
The Locale portion of LocalizedText values shall be encoded as a JSON string. The field is not encoded if it is null or empty. |
Text |
The Text portion of LocalizedText values shall be encoded as a JSON string. The field is not encoded if it is null or empty. |
The ExtensionObject is encoded as a JSON object as described in:
with the UaTypeId field inserted into the JSON object.
Decoders shall report decoding errors to the application if a JSON object has multiple fields with the same name.
The UaEncoding and UaBody fields are only used when UA Binary or UA XML encoded Structures are serialized. Only the fields in Table 39 are present if these fields are used.
The JSON object fields used for an ExtensionObject are in Table 39.
Table 39 – JSON Object Fields used for an ExtensionObject
Name |
Description |
UaTypeId |
A NodeId formatted using the rules in 5.4.2.10. This is the NodeId of a DataType Node. |
UaEncoding |
A JSON number that represents the format of the UaBody field. A value of 1 indicates UA Binary data is encoded in the UaBody field. A value of 2 indicates UA XML data is encoded in the UaBody field. This field is omitted for JSON encoded Structures. |
UaBody |
A ByteString containing an UA Binary or UA XML encoded Structure. This field is omitted for JSON encoded Structures. |
Encoders, when allowed by the DevelopmentPlatform, should write the UaTypeId first. Decoders shall accept the UaTypeId in any position.
Variant values shall be encoded as a JSON object with the fields shown in Table 40.
Table 40 – JSON Object Definition for a Variant
Name |
Description |
UaType |
The Built-in type for the value contained in the Body (see Table 1) encoded as JSON number. |
Value |
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 JSON array containing all elements. The mapping of a multidimensional array to a flat list is described in 5.2.2.16. The field is not encoded if the value is a NULL for nullable Built-in types (see Table 1). |
Dimensions |
The dimensions of the array encoded as a JSON array of JSON numbers. |
Encoders, when allowed by the DevelopmentPlatform, should write the UaType first. Decoders shall accept the UaType field in any position.
DataValue values shall be encoded as a JSON object with the fields shown in Table 41.
The DataValue adds additional fields to a Variant (see 5.4.2.17).
Table 41 – JSON Object Definition for a DataValue
Name |
Data Type |
Description |
UaType |
Byte |
|
Value |
* |
|
Dimensions |
UInt32 |
See the Dimensions field in the Variant. |
Status |
StatusCode |
The status associated with the value. Not encoded if the value is Good (0). |
SourceTimestamp |
DateTime |
The source timestamp associated with the value. Not encoded if the value is DateTime.MinValue. |
SourcePicoseconds |
UInt16 |
The number of 10 Picosecond intervals for the SourceTimestamp. Not encoded if the value is 0. |
ServerTimestamp |
DateTime |
The Server timestamp associated with the value. Not encoded if the value is DateTime.MinValue. |
ServerPicoseconds |
UInt16 |
The number of 10 Picosecond intervals for the ServerTimestamp. Not encoded if the value is 0. |
Decimal values shall be encoded as a JSON object with the fields in Table 42.
Table 42 – JSON Object Definition for a Decimal
Name |
Description |
Scale |
A JSON number with the scale applied to the Value. |
Value |
A JSON string with the Value encoded as a base-10 signed integer. (See the XML encoding of Integer values described in 5.3.1.3). |
See 5.1.10 for a description of the Scale and Value fields.
When encoding in a Variant, a Decimal value shall be encoded as an ExtensionObject with the JSON object in Table 42 as the Body. The TypeId shall be the NodeId of the Decimal DataType and the Encoding shall be 0.
Enumeration values shall be encoded as a JSON number.
When an Enumeration is encoded in a Variant the Type field is Int32.
Enumeration values are encoded as a JSON string with the following format:
<name>_<value>
Where the name is the enumeration literal and the value is the numeric value.
If the literal is not known to the encoder, the numeric value is encoded as a JSON string.
When an Enumeration is encoded in a Variant the Type field is a Int32 and the value is encoded as a JSON number.
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.
Multidimensional Arrays are encoded as JSON object with the fields defined in Table 43.
Table 43 – JSON Object Definition for an inline Matrix
Name |
Description |
Array |
Multi-dimensional arrays are encoded as a one-dimensional JSON array which is reconstructed using the value of the Dimensions field (see 5.2.2.16). |
Dimensions |
The dimensions of the array encoded as a JSON array of JSON numbers. |
Structures shall be encoded as JSON objects.
Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Structure is serialized in JSON.
Fields which are NULL or have a default value shall be encoded using the rules shown in Table 44.
Table 44 – JSON Encoding Rules for Structures
Field Value |
Compact |
Verbose |
NULL |
Omitted |
JSON null |
Default Value |
Omitted |
Default Value |
For example, instances of the structures:
struct Type2
{
Int32 A;
Int32 B;
Char* C;
};
struct Type1
{
Int32 X;
Int32 NoOfY;
Type2* Y;
Int32 Z;
};
The CompactEncoding is represented in JSON as:
{
"X":1234,
"Y":[ { "A":1, "B":2, "C":"Hello" }, { "A":3, "B":4 } ],
"Z":5678
}
Where “C” is omitted from the second Type2 instance because it has a NULL value.
The VerboseEncoding is represented in JSON as:
{
"X":1234,
"Y":[ { "A":1, "B":2, "C":"Hello" }, { "A":3, "B":4, "C":null } ],
"Z":5678
}
Where “C” in the second Type2 instance has a JSON null value.
Code generators should ensure that the special field names (UaType, UaTypeId and EncodingMask) are not permitted in Structures.
Structures with optional fields shall be encoded as JSON objects as shown in Table 45.
Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Structure is serialized in JSON. The EncodingMask may not appear as the first field.
In the VerboseEncoding the bits in the EncodingMask are determined by the presence of a field in the JSON object. In the CompactEncoding, EncodingMask indicates which fields are specified because fields are omitted because they have a default value.
Table 45 – JSON Object Definition for a Structures with Optional Fields
Name |
Description |
EncodingMask |
A bit mask indicating what fields are encoded in the structure (see 5.2.7) This mask is encoded as a JSON number. The bits are sequentially assigned to optional fields in the order that they are defined. This field is omitted in the VerboseEncoding |
<FieldName> |
The field in structure encoded according to the rules defined for their DataType. One entry may exist for each mandatory field and each optional field that is present. |
Fields which are NULL or have a default value shall be encoded using the rules shown in Table 46.
Table 46 – JSON Encoding Rules for Structures with Optional Fields
Field Value |
Field Type |
Compact |
Verbose |
NULL |
Mandatory |
Omitted |
JSON null |
Default Value |
Mandatory |
Omitted |
Default Value |
NULL |
Optional (Present) |
Omitted |
JSON null |
Default Value |
Optional (Present) |
Omitted |
Default Value |
NULL |
Optional (Omitted) |
Omitted |
Omitted |
Default Value |
Optional (Omitted) |
Omitted |
Omitted |
If a Structure with optional fields is subtyped, the subtypes extend the EncodingMask defined for the parent.
The following is an example of a structure with optional fields using C++ syntax:
struct TypeA
{
Int32 X;
Int32* O1;
SByte Y;
Int32* O2;
};
O1 and O2 are optional fields where a NULL indicates that the field is not present.
Assume that O1 is not specified and the value of O2 is 0.
The CompactEncoding would be:
{ "EncodingMask": 2, "X": 1, "Y": 2 }
Where decoders would assign the default value of 0 to O2 since the mask bit is set even though the field was omitted (this is the behaviour defined for the Int32 DataType). Decoders would mark O1 as ‘not specified’.
The VerboseEncoding would be:
{ "X": 1, "Y": 2, "O2": 0 }
Encoders, when allowed by the DevelopmentPlatform, should write the EncodingMask first. Decoders shall accept the EncodingMask in any position.
Code generators should ensure that the special field names (UaType, UaTypeId and EncodingMask) are not permitted in Structures with option field.
Unions shall be encoded as JSON objects as shown in Table 47.
Note that JSON objects are unordered sets of name-value pairs. The order specified by the DataTypeDefinition is not preserved when a Union is serialized in JSON.
Table 47 – JSON Object Definition for a Union
Name |
Description |
SwitchField |
This field is only present in the CompactEncoding. The identifier for the field in the Union which is encoded as a JSON number. The valid values for this field follow the conventions defined in 5.2.8. A Union with no field specified is encoded as an empty JSON object. |
<FieldName > |
The value of the field encoded using the rules that apply to the DataType. The name of field in the JSON object is the name of the field in the DataTypeDefinition.
|
For example, instances of the union:
struct Union1
{
Byte Selector;
{
Int32 A;
Double B;
Char* C;
}
Value;
};
would be represented in the CompactEncoding as:
{ "SwitchField":2, "B":3.1415 }
and would be represented in the VerboseEncoding as:
{ "B":3.1415 }
Messages are encoded ExtensionObjects (see 5.4.2.16).