npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-opcua-json

v2.187.16

Published

pure nodejs OPCUA SDK - the OPC UA JSON encoding of OPC 10000-6 clause 5.4

Readme

node-opcua-json

The OPC UA JSON encoding, OPC 10000-6 clause 5.4: encoders and decoders for every built-in type, for Variant, DataValue and ExtensionObject, across all four encoding schemes the specification has defined.

see http://node-opcua.github.io/

npm install node-opcua-json

Why

JSON is how OPC UA data leaves the OPC UA world. It is the wire form of PubSub over MQTT and AMQP (OPC 10000-14), the form a PubSubConfigurationDataType is exchanged in, and the form a Variant takes inside an Annex I JSON nodeset. Anywhere a value has to survive a hop through a broker, a database or another vendor's stack, this is the encoding it travels in.

The details are easy to get subtly wrong: a UInt64 is a string, not a number; a NodeId carries a namespace URI, not the index it had in the source document; and the field names changed between 1.04 and 1.05. This package is the single place the SDK implements clause 5.4, so every consumer agrees on those answers.

Quick start

import { DataType, Variant } from "node-opcua-variant";
import { JsonEncodingScheme, opcuaJsonEncodeVariant } from "node-opcua-json";

const namespaceArray = ["http://opcfoundation.org/UA/"];
const v = new Variant({ dataType: DataType.Byte, value: 42 });

opcuaJsonEncodeVariant(v, JsonEncodingScheme.Compact, namespaceArray);
// { UaType: 3, Value: 42 }

opcuaJsonEncodeVariant(v, JsonEncodingScheme.DeprecatedReversible, namespaceArray);
// { Type: 3, Body: 42 }

opcuaJsonEncodeVariant(v, JsonEncodingScheme.DeprecatedNonReversible, namespaceArray);
// 42

The namespaceArray is not decoration. NodeIds and QualifiedNames encode with their namespace URI, so both directions need the document's namespace table to map an index to a URI and back. Pass the array the document declares, not the one your address space happens to have.

The four schemes

Part 6 has changed JSON encoding once, and both generations are in the field, so both are here.

| JsonEncodingScheme | version | a Variant looks like | |---|---|---| | DeprecatedNonReversible | 1.04 | the bare value, with no type at all | | DeprecatedReversible | 1.04 | { Type, Body, Dimensions? } | | Verbose | 1.05 | { UaType, Value, UaDimensions? }, every field written | | Compact | 1.05 | the same, with every field equal to its type's default omitted |

JsonEncoderMode104 (Reversible / NonReversible) and JsonEncoderMode105 (Verbose / Compact) are aliases into that same enum, so a 1.05-only API can take a parameter that cannot express a 1.04 mode. toJsonEncodingScheme() widens either back.

Two consequences worth knowing before you diff output against another stack:

  • The 1.05 field names are UaType / Value / UaDimensions. They changed with the encoding, so a 1.05 reader will not find Type or Body.
  • Compact omits defaults, so a Byte of 0 encodes as { UaType: 3 } with no Value at all, while Verbose writes { UaType: 3, Value: 0 }. Decoding is unaffected; byte-for-byte comparison against a Verbose writer is not.

When a PubSub subscriber has to work out which scheme it is being sent, JsonDataSetMessageContentMaskToJsonEncodingScheme reads the two FieldEncoding bits of OPC 10000-14 Table 112, and JsonEncodingSchemeToJsonDataSetMessageContentMask writes them.

One edition only: node-opcua-json/104 and node-opcua-json/105

The root entry point is deliberately flexible: it takes a JsonEncodingScheme and dispatches to whichever edition it names. Code that speaks a single edition, such as a PubSub publisher pinned to 1.05 or a bridge to a 1.04-only stack, is better off importing that edition directly.

import { opcuaJsonEncodeDataValue, JsonEncoderMode, type DataValueJSON } from "node-opcua-json/105";

const pojo: DataValueJSON = opcuaJsonEncodeDataValue(dataValue, JsonEncoderMode.Compact, namespaceArray);
// { UaType: 11, Value: 3.5, ... } and nothing else can come out

Each subpath exposes the whole API under version-free names: opcuaJsonEncodeDataValue, opcuaJsonDecodeVariant, opcuaJsonEncodeStatusCode, opcuaJsonEncodeNodeId, opcuaJsonEncodeExtensionObject, the DataValueJSON / VariantJSON / NodeIdJSON / ExtensionObjectJSON types and a JsonEncoderMode enum, every one bound to that edition alone. The mode parameter is the edition's own two-value enum, so a 1.04 mode is a type error in the 1.05 realm and vice versa, and the JSON types are the edition's exact shapes rather than a union. Version-agnostic scalars (Int64, ByteString, DateTime, ...) are the same functions in both.

Switching an existing file from one edition to the other is a one-line change of the import path. The root package keeps the suffixed names (opcuaJsonEncodeDataValue104, opcuaJsonEncodeVariant105, ...) and also exposes both realms as the v104 and v105 namespaces for code that has to handle both in one place.

Decoding structures

Decoding a Variant that contains a structure means constructing that structure, and only the caller knows how to find its class. That is the whole of the ExtensionObjectBuilder interface:

interface ExtensionObjectBuilder {
    getExtensionObjectConstructor(dataTypeNodeId: NodeId): ExtensionObjectConstructorFuncWithSchema;
}
import { opcuaJsonDecodeVariant, type VariantJSON105 } from "node-opcua-json";

const variant = opcuaJsonDecodeVariant(json as VariantJSON105, builder, namespaceArray);

For a model with custom structures, builder resolves the DataType NodeId against a live address space or a dynamically built factory. For a model with only built-in types, a builder that throws is enough, because it is never reached.

Handing the whole thing over at once, when you already know the type:

import { PubSubConnectionDataType } from "node-opcua-types";
import { decodeOPCUA_JSON } from "node-opcua-json";

const config = decodeOPCUA_JSON(PubSubConnectionDataType, JSON.parse(text));
config.address; // a NetworkAddressUrlDataType, reconstructed from its UaTypeId

The test suite reads configuration files produced by the .NET stack, so this path is checked against another implementation rather than only against itself.

API

Every built-in type has an opcuaJsonEncodeX / opcuaJsonDecodeX pair, and each takes the scheme, so one call site serves all four.

| area | exports | |---|---| | Variant | opcuaJsonEncodeVariant, opcuaJsonDecodeVariant, the 104 / 105 pairs, VariantJSON, VariantJSON104, VariantJSON105 | | DataValue | opcuaJsonEncodeDataValue, opcuaJsonDecodeDataValue, opcuaJsonEncodeDataValueMQTT, the 104 / 105 variants of all three, DataValueJSON, DataValueJSON104, DataValueJSON105 | | ExtensionObject | opcuaJsonEncodeExtensionObject, opcuaJsonDecodeExtensionObject, the ...Body pair, makeBody, ExtensionObjectJSON104, ExtensionObjectJSON105 | | NodeId | opcuaJsonEncodeNodeId, opcuaJsonDecodeNodeId, the ExpandedNodeId pair, the 104 / 105 encoders, opcuaJsonEncodeNodeIdAsString | | realms | node-opcua-json/104, node-opcua-json/105, and the v104 / v105 namespaces on the root | | scalars | Int64, UInt64, ByteString, DateTime, LocalizedText, QualifiedName, StatusCode | | structures | decodeOPCUA_JSON, decodeOPCUA_JSONOld | | schemes | JsonEncodingScheme, JsonEncoderMode104, JsonEncoderMode105, toJsonEncodingScheme, makeDataValueEncodingEnum, the two JsonDataSetMessageContentMask converters | | dispatch | bodyEncodeFunctor(dataType), bodyDecodeFunctor(dataType), EncoderFunc, DecoderFunc, ExtensionObjectBuilder |

opcuaJsonEncodeVariant and opcuaJsonDecodeVariant are overloaded across all four schemes and so cannot express the 1.05 object shape for Verbose alone. Where you need that exact type, reach for the explicit opcuaJsonEncodeVariant105 / opcuaJsonDecodeVariant105, or the matching ...104 pair.

Position in the SDK

This package sits low in the dependency graph, on the type and factory packages and nothing else. It does not depend on node-opcua-address-space, which is what allows the address space to depend on it, and in turn lets node-opcua-uanodeset-json encode nodeset values with the same codec the PubSub layer uses.

The ExtensionObjectBuilder parameter is the price of that position: rather than reaching for an address space itself, the package asks the caller to resolve structures.

Provenance and licence

This code was written for @sterfive/node-opcua-json, part of the node-opcua-pubsub work, and is contributed here by its copyright holder under MIT, so that JSON encoding is not a licensing question for anyone building on the SDK.

MIT