fwCtrlUtils  8.5.0
CtrlJsonSerialize Programmer Manual

CtrlJsonSerialize can serialize and deserialize most of WinCC OA variable types inluding mappings, dyn_ arrays and vectors. In addition it works for user defined classes, structures and enumerated types. Nested objects are also supported.

An example string containing JSON formatted data is presented below:

{
"i": 5,
"s": "demo",
"b": true,
"df": [
3.91,
0,
0.5
]
}

That string can be a result of serialization of the object presented below:

#uses "CtrlJsonSerialization"
struct Demo{
int i;
string s;
bool b;
dyn_float df;
};
main(){
Demo d;
d.i = 5;
d.s = "demo";
d.b = true;
d.df = makeDynFloat(3.91, 0, 0.5);
DebugTN(fwJsonSerialize(d, makeDynString(), false));
}

JSON format and its limitations

JSON format supports only limited number of data types. These are:

  • Primitive (simple):
    • Number (double precision floating point number)
    • String (double-qouted, with backslash escaping)
    • Boolean (either true or false)
    • Empty (null)
  • Compound:
    • Array (ordered sequence of values)
    • Object (collection of key - value pairs)

Please note, that when data is serialized to JSON, an information about its type is lost, if this type is not one of the simple types listed above.

If the data is serialized from, and later deserialized to an object, where all properties have known type, this peculiarity does not need to be considered. In such case, during deserialization, the variable type of particular object property is used to resolve the data type of JSON value encoding this property.

In the example above, value of key "i" is deserialized to an integer value, even thogh in JSON it is treated as a double precision floating point number. This is because for object of type 'Demo', property 'i' is an integer.

An opposite situation occurs, when a mapping, where each key has a value of type 'int', is serialized. An attempt to deserialize it back to a mapping, results in a mapping containing same keys and values, but these values are of type 'float'.

#uses "CtrlJsonSerialization"
main(){
mapping m = makeMapping("a", (int)30);
string json = fwJsonSerialize(m);
mapping deserialized_m;
fwJsonDeserialize(json, deserialized_m);
DebugTN(getTypeName(m["a"])); // prints: "float"
}

Below are listed example data types, for which original data type(s) may be lost pernamently after serialization (there is no possibility to resolve original type during deserialization):

  • anytype, mixed (if initialized with value)
  • dyn_anytype, dyn_mixed initialized elements
  • values in mapping's key-value pairs
  • vector<anytype>, vector<mixed>, vector<void>
  • shared_ptr<anytype>, shared_ptr<mixed>, shared_ptr<void>
  • shared_ptr<BaseClass> when contains pointer to DerivedClass

This may be addressed in future releases of CtrlJsonSerialize by providing option to include data type annotation in the JSON string containing serialized object.

CtrlJsonSerialize and WinCC OA JSON support

WinCC OA already provides funcitons for data serialization and deserialization to/from JSON format. These two funtions are jsonEncode() and jsonDecode(). However they offer only basic, limited functionality. As an example jsonEncode() does cannot handle serialization of nested ctrl++ objects and jsonDecode() returns value of type of JSON encoded data. For ctr++ objects this is a mapping, which forces class developer to write an additional code for providing effective deserialization of JSON to object of this class. Such code needs to be maintained when particular class evolves and needs to be provided for each class separately.

Given that, a decision was taken to implement functions offering JSON serialization and deserialization in scope of the JCOP framework. This allows to provide the same base functionality as WinCC OA functions do, overcome currently identified limitations and have possibility to extend it in the future according to the demands of the JCOP framework users community.

At current stage CtrlJsonSerialize functions are compatible with WinCC OA equivalents for most of the types. In addition fwJsonSerialize() allows for serialization of nested ctrl++ objects and fwJsonDeserialize(), that returns deserialized value through an output argument, allows direct deserialization of JSON encoded value into the the desired type.

WinCC OA types serialization

Please find below a table presenting how different WinCC OA data types are encoded in JSON format by fwJsonSerialize() function.

Ctrl data typeValue type in JSONEncoding exampleRemarks
anytypeEmpty[null]Empty anytype (before first assignment), otherwise as for assigned type
mixedEmpty[null]Empty mixed (before first assignment), otherwise as for assigned type
boolBoolean[true]
intNumber[-4562]
uintNumber[924]
longNumber[4873475273]Loss of precision may happen - see below
ulongNumber[224835936543]Loss of precision may happen - see below
float/doubleNumber[9375.378457]NaN, +/-Inf not supported - serialized as Empty value
charString["66"]ASCII character code in decimal form, example:"66" for 'B'
stringString["example"]
timeString["2020-03-17T09:08:03.707Z"]UTC time in Extended Date/Time Format
bit32String["110(...)100"]32-bit pattern value in binary form
bit64String["11010(...)10100"]64-bit pattern value in binary form
blobString["FFAB003AFB"]Each blob byte represented by 2 characters (representing hexadecimal number)
mappingObject{"a":5,"b":null}Only mapping keys of type 'string' can be serialized, keys of any other type are discarded in serialized Object
dyn_ arrayArray["text", 30, bool]
dyn_dyn_ arrayArray[["a", "b"], []]
shared_ptr<type>Empty[null]Uninitialized shared_ptr<type> (nullptr), otherwise as for type it points to
vector<type>Array[45, 0, 99]
ctrl++ enumerated typeNumber[5]Number representing enum item
ctrl++ class/structObject{"a":40,"b":true}Collection of instance property name(string):value pairs
function_ptrString"MyClass::method"Function name. For methods, a name is prepended by a scope name and delimited with '::'. nullptr is serialized to an empty string (note: jsonEncode() serializes it as "-null-"). Currently only serialization is supported

In addition support for atime and errClass types is provided. As these types are rarely used, they are not included in this documentation. Serialization and deserialization is not provided for langString type.

Warning
64-bit integer values (long, ulong) may loose their precision after serialization to JSON. This is because they are encoded as double precision floating point number. The integer limits for this type is 2^53 (-9007199254740992 to +9007199254740992). Values outside of this range will be serialized with reduced precision.

Remarks on JSON deserialization

Target object for deserialization is given as an output argument for fwJsonDeserialize(). Deserialized JSON values takes the type of given variable and its possible properties (eg. array elements or member variables of ctrl++ class). However there are variable types, that do not impose particular data type on the deserialized value. These are:

  • anytype, mixed
  • elements of dyn_anytype, dyn_mixed
  • elements of vector>anytype<, vector>mixed<, vector>void<
  • shared_ptr>anytype<, shared_ptr>mixed<, shared_ptr>void<
  • mapping values

When such variable is encountered by deserialization function, the value type, that is assigned to this variable, is resolved from JSON value type. Table below presents mapping between JSON value types and WinCC OA data types to which given JSON value is assigned.

Value type in JSONCtrl data type
Emptyanytype (unassigned)
Booleanbool
Numberfloat
Stringstring
Arraydyn_anytype
Objectmapping

Please note, that JSON format requires to always enclose primitive (simple) values at JSON root level in square brackets. For example: '[5]' instead of only '5' - it is not a valid JSON. Therefore, when primitive value at JSON root level (such JSON contains effectively only this single value) is deserialized to variable of type anytype, this variable takes the new type: dyn_anytype. It is an expected behavior. It comes from the fact, that in such case it is not possible to determine, whether primitive value enclosed in square brackets should be treated as a primitive or as an array and by default a square brackets represents an array in JSON format.

#uses "CtrlJsonSerialization"
main(){
anytype a;
fwJsonDeserialize("[5]", a);
DebugTN(getTypeName(a)); // prints: "dyn_anytype"
}