OPC UA interoperability notes
OPC UA defines a common information model and service set, but the same concept can appear in different wire formats, files, and server address spaces. This page explains the parts that most often cause implementation mistakes and shows how to handle them in a predictable way.
NodeId representation depends on the encoding
A NodeId identifies a node within a namespace. The logical value is the same concept across OPC UA, but the encoded representation is not identical in every format.
For example, OPC UA JSON encoding and UADP/UA Binary encoding use different NodeId identifier type values:
Identifier type |
JSON value |
UADP/UA Binary value |
|---|---|---|
|
not used |
0 |
|
not used |
1 |
|
0 |
2 |
|
1 |
3 |
|
2 |
4 |
|
3 |
5 |
The two lists look similar, but they are not the same enum. A JSON NodeId type must not be decoded with the UADP or UA Binary NodeId enum, and UADP or UA Binary values must not be decoded with the JSON enum.
There is an apparent numeric relationship for some values, such as
UadpType = JsonType + 2 for Numeric, String, Guid, and ByteString. Treat
this only as an observation, not as an implementation rule. It does not cover
the compact TwoByte and FourByte binary forms and it makes the decoder depend
on a shortcut instead of the active encoding.
Compact binary NodeIds are encoding choices
The binary encodings can represent some numeric NodeIds with compact forms:
Binary form |
When it can be used |
|---|---|
|
Namespace |
|
Namespace indexes in the range |
|
Larger namespace indexes and numeric identifiers. |
Do not infer the original binary NodeId form from the numeric identifier alone. The same logical NodeId can often be encoded in more than one valid binary form. A decoder should preserve the logical NodeId value. An encoder may choose the smallest valid binary form unless a specific format is required by the caller or by an interoperability test.
Recommended implementation pattern:
Decode each transport format with its own format-specific parser.
Convert decoded values into one internal NodeId representation.
Encode from that internal representation using the target wire format.
Keep JSON, UADP, and UA Binary enum handling separate.
Namespace indexes are local to an address space
A NodeId contains a namespace index. That index is not a global identifier. It is an index into the server’s namespace table, so the same companion specification can have different indexes on different servers.
For example, two servers may expose the same type of machine and the same companion specification, but load NodeSet2 XML files in a different order. The address-space hierarchy may be equivalent while the namespace indexes in NodeIds are different.
Applications should avoid treating namespace indexes as stable across servers. Use one of these strategies instead:
Resolve the namespace URI from the server namespace table before comparing or storing NodeIds outside the current server connection.
Store external references as ExpandedNodeIds or as
namespaceUriplus identifier.Use browse paths or
TranslateBrowsePathsToNodeIdswhen a stable model path is more appropriate than a hard-coded NodeId.Load companion specifications in a deterministic order when you control the server configuration.
NodeSet2 XML imports require namespace remapping
NodeSet2 XML files contain a namespace table and NodeIds that refer to entries in that table. The namespace indexes inside a file are local to that file. When the file is imported into a server model, those indexes must be mapped to the indexes assigned by the target address space.
The default OPC UA namespace has index 0. Servers also commonly reserve a
local application namespace, often index 1. Imported companion
specifications are then assigned indexes according to the model loader’s
namespace table.
Correct import logic must therefore:
Read the namespace URIs declared by the XML file.
Create or find the corresponding namespaces in the target model.
Rewrite NodeIds, reference targets, type definitions, data type references, and aliases from XML-local indexes to target-model indexes.
Preserve the namespace URI as the stable identity of the namespace.
This remapping is expected OPC UA behavior, not a data corruption issue. A NodeId string copied directly from a NodeSet2 file may not be valid in the same form after import unless the namespace index happens to be the same.
Practical guidance
For robust OPC UA applications:
Treat NodeIds as structured values, not as plain strings.
Treat namespace URI as the stable namespace identity.
Use namespace indexes only after resolving them in the current server or model.
Keep transport-specific encoding code separate from model-level NodeId logic.
Prefer browse paths or well-known type definitions when connecting to servers that may load companion specifications in different orders.