Browse Source

JSON: added networkmessage and datasetmessage json encoding (PUBSUB)

Lukas M 5 years ago
parent
commit
89b41278ab

+ 3 - 0
CMakeLists.txt

@@ -639,6 +639,9 @@ if(UA_ENABLE_PUBSUB)
 endif()
 
 if(UA_ENABLE_JSON_ENCODING)
+    if(UA_ENABLE_PUBSUB)
+        list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/src/pubsub/ua_pubsub_networkmessage_json.c)
+    endif()
     list(APPEND internal_headers ${PROJECT_SOURCE_DIR}/deps/jsmn/jsmn.h
                                  ${PROJECT_SOURCE_DIR}/deps/string_escape.h
                                  ${PROJECT_SOURCE_DIR}/deps/itoa.h

+ 1 - 0
include/ua_config.h.in

@@ -36,6 +36,7 @@
 #cmakedefine UA_ENABLE_HISTORIZING
 #cmakedefine UA_ENABLE_EXPERIMENTAL_HISTORIZING
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS_EVENTS
+#cmakedefine UA_ENABLE_JSON_ENCODING
 
 /* Multithreading */
 #cmakedefine UA_ENABLE_MULTITHREADING

+ 161 - 35
src/pubsub/ua_pubsub.c

@@ -751,10 +751,25 @@ UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMess
     if(!dataSetMessage->data.keyFrameData.dataSetFields)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
+#ifdef UA_ENABLE_JSON_ENCODING
+    /* json: insert fieldnames used as json keys */
+       dataSetMessage->data.keyFrameData.fieldNames =
+               (UA_String *)UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_STRING]);
+       if(!dataSetMessage->data.keyFrameData.fieldNames)
+           return UA_STATUSCODE_BADOUTOFMEMORY;
+#endif
+
     /* Loop over the fields */
     size_t counter = 0;
     UA_DataSetField *dsf;
     LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
+
+#ifdef UA_ENABLE_JSON_ENCODING
+        /* json: store the fieldNameAlias*/
+        UA_String_copy(&dsf->config.field.variable.fieldNameAlias,
+              &dataSetMessage->data.keyFrameData.fieldNames[counter]);
+#endif
+
         /* Sample the value */
         UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter];
         UA_PubSubDataSetField_sampleValue(server, dsf, dfv);
@@ -879,7 +894,11 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
     /* Reset the message */
     memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
 
-    /* Currently is only UADP supported. The configuration Flags are included
+    /* store messageType to switch between json or uadp (default) */
+    UA_UInt16 messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE;
+    UA_JsonDataSetWriterMessageDataType *jsonDataSetWriterMessageDataType = NULL;
+
+    /* The configuration Flags are included
      * inside the std. defined UA_UadpDataSetWriterMessageDataType */
     UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
     UA_UadpDataSetWriterMessageDataType *dataSetWriterMessageDataType = NULL;
@@ -888,7 +907,18 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
        (dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE])) {
         dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *)
             dataSetWriter->config.messageSettings.content.decoded.data;
-    } else {
+
+        /* type is UADP */
+        messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE;
+    } else if((dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED ||
+        dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
+       (dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE])) {
+        jsonDataSetWriterMessageDataType = (UA_JsonDataSetWriterMessageDataType *)
+            dataSetWriter->config.messageSettings.content.decoded.data;
+
+            /* type is JSON */
+            messageType = UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE;
+    }else {
         /* create default flag configuration if no
          * UadpDataSetWriterMessageDataType was passed in */
         memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetWriterMessageDataType));
@@ -921,41 +951,72 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
         dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
     }
 
-    /* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
-        dataSetMessage->header.configVersionMajorVersionEnabled = true;
-        dataSetMessage->header.configVersionMajorVersion =
-            currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
-    }
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
-        dataSetMessage->header.configVersionMinorVersionEnabled = true;
-        dataSetMessage->header.configVersionMinorVersion =
-            currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
-    }
+    if(messageType == UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE){
+        /* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
+            dataSetMessage->header.configVersionMajorVersionEnabled = true;
+            dataSetMessage->header.configVersionMajorVersion =
+                currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
+        }
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
+            dataSetMessage->header.configVersionMinorVersionEnabled = true;
+            dataSetMessage->header.configVersionMinorVersion =
+                currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
+        }
 
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
-        dataSetMessage->header.dataSetMessageSequenceNrEnabled = true;
-        dataSetMessage->header.dataSetMessageSequenceNr =
-            dataSetWriter->actualDataSetMessageSequenceCount;
-    }
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
+            dataSetMessage->header.dataSetMessageSequenceNrEnabled = true;
+            dataSetMessage->header.dataSetMessageSequenceNr =
+                dataSetWriter->actualDataSetMessageSequenceCount;
+        }
 
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
-        dataSetMessage->header.timestampEnabled = true;
-        dataSetMessage->header.timestamp = UA_DateTime_now();
-    }
-    /* TODO: Picoseconds resolution not supported atm */
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
-        dataSetMessage->header.picoSecondsIncluded = false;
-    }
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
+            dataSetMessage->header.timestampEnabled = true;
+            dataSetMessage->header.timestamp = UA_DateTime_now();
+        }
+        /* TODO: Picoseconds resolution not supported atm */
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
+            dataSetMessage->header.picoSecondsIncluded = false;
+        }
+
+        /* TODO: Statuscode not supported yet */
+        if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
+            dataSetMessage->header.statusEnabled = false;
+        }
+    }else if(messageType == UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE){
+        if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION){
+            dataSetMessage->header.configVersionMajorVersionEnabled = true;
+            dataSetMessage->header.configVersionMajorVersion =
+                currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
+        }
+        if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION){
+            dataSetMessage->header.configVersionMinorVersionEnabled = true;
+            dataSetMessage->header.configVersionMinorVersion =
+                currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
+        }
 
-    /* TODO: Statuscode not supported yet */
-    if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
-        dataSetMessage->header.statusEnabled = false;
+        if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
+            dataSetMessage->header.dataSetMessageSequenceNrEnabled = true;
+            dataSetMessage->header.dataSetMessageSequenceNr =
+                dataSetWriter->actualDataSetMessageSequenceCount;
+        }
+
+        if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_TIMESTAMP) {
+            dataSetMessage->header.timestampEnabled = true;
+            dataSetMessage->header.timestamp = UA_DateTime_now();
+        }
+
+        /* TODO: Statuscode not supported yet */
+        if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_STATUS){
+            dataSetMessage->header.statusEnabled = false;
+        }
     }
 
     /* Set the sequence count. Automatically rolls over to zero */
     dataSetWriter->actualDataSetMessageSequenceCount++;
 
+    /* JSON does not differ between deltaframes and keyframes, only keyframes are currently used. */
+    if(messageType != UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE){
 #ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
     /* Check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame */
     if(dataSetWriter->connectedDataSetVersion.majorVersion != currentDataSet->dataSetMetaData.configurationVersion.majorVersion ||
@@ -991,11 +1052,61 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
 
     dataSetWriter->deltaFrameCounter = 1;
 #endif
+    }
 
     UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
     return UA_STATUSCODE_GOOD;
 }
 
+static UA_StatusCode
+sendNetworkMessageJson(UA_PubSubConnection *connection, UA_DataSetMessage *dsm,
+                   UA_UInt16 *writerIds, UA_Byte dsmCount) {
+   UA_StatusCode retval = UA_STATUSCODE_BADNOTSUPPORTED;
+#ifdef UA_ENABLE_JSON_ENCODING
+    UA_NetworkMessage nm;
+    memset(&nm, 0, sizeof(UA_NetworkMessage));
+    nm.version = 1;
+    nm.networkMessageType = UA_NETWORKMESSAGE_DATASET;
+    nm.payloadHeaderEnabled = true;
+
+    nm.payloadHeader.dataSetPayloadHeader.count = dsmCount;
+    nm.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = writerIds;
+    nm.payload.dataSetPayload.dataSetMessages = dsm;
+
+    /* Allocate the buffer. Allocate on the stack if the buffer is small. */
+    UA_ByteString buf;
+    size_t msgSize = UA_NetworkMessage_calcSizeJson(&nm, NULL, 0, NULL, 0, true);
+    size_t stackSize = 1;
+    if(msgSize <= UA_MAX_STACKBUF)
+        stackSize = msgSize;
+    UA_STACKARRAY(UA_Byte, stackBuf, stackSize);
+    buf.data = stackBuf;
+    buf.length = msgSize;
+    if(msgSize > UA_MAX_STACKBUF) {
+        retval = UA_ByteString_allocBuffer(&buf, msgSize);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+    }
+
+    /* Encode the message */
+    UA_Byte *bufPos = buf.data;
+    memset(bufPos, 0, msgSize);
+    const UA_Byte *bufEnd = &buf.data[buf.length];
+    retval = UA_NetworkMessage_encodeJson(&nm, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
+    if(retval != UA_STATUSCODE_GOOD) {
+        if(msgSize > UA_MAX_STACKBUF)
+            UA_ByteString_deleteMembers(&buf);
+        return retval;
+    }
+
+    /* Send the prepared messages */
+    retval = connection->channel->send(connection->channel, NULL, &buf);
+    if(msgSize > UA_MAX_STACKBUF)
+        UA_ByteString_deleteMembers(&buf);
+#endif
+    return retval;
+}
+
 static UA_StatusCode
 sendNetworkMessage(UA_PubSubConnection *connection, UA_DataSetMessage *dsm,
                    UA_UInt16 *writerIds, UA_Byte dsmCount) {
@@ -1065,8 +1176,9 @@ UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) {
     if(writerGroup->writersCount <= 0)
         return;
 
-    /* Binary encoding? */
-    if(writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP) {
+    /* Binary or Json encoding?  */
+    if(writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP
+            && writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_JSON) {
         UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
                        "Publish failed: Unknown encoding type.");
         return;
@@ -1119,8 +1231,14 @@ UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) {
          * are contained in the PublishedDataSet, then this DSM must go into a
          * dedicated NM as well. */
         if(pds->promotedFieldsCount > 0 || maxDSM == 1) {
-            res = sendNetworkMessage(connection, &dsmStore[dsmCount],
-                                     &dsw->config.dataSetWriterId, 1);
+            if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP){
+                res = sendNetworkMessage(connection, &dsmStore[dsmCount],
+                                         &dsw->config.dataSetWriterId, 1);
+            }else if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON){
+                res = sendNetworkMessageJson(connection, &dsmStore[dsmCount],
+                                         &dsw->config.dataSetWriterId, 1);
+            }
+
             if(res != UA_STATUSCODE_GOOD)
                 UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
                                "PubSub Publish: Could not send a NetworkMessage");
@@ -1138,8 +1256,16 @@ UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) {
         UA_Byte nmDsmCount = maxDSM;
         if(i == nmCount - 1)
             nmDsmCount = (UA_Byte)dsmCount % maxDSM;
-        UA_StatusCode res3 = sendNetworkMessage(connection, &dsmStore[i * maxDSM],
-                                                &dsWriterIds[i * maxDSM], nmDsmCount);
+
+        UA_StatusCode res3 = UA_STATUSCODE_GOOD;
+        if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP){
+            res3 = sendNetworkMessage(connection, &dsmStore[i * maxDSM],
+                                                            &dsWriterIds[i * maxDSM], nmDsmCount);
+        }else if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON){
+            res3 = sendNetworkMessageJson(connection, &dsmStore[i * maxDSM],
+                                                            &dsWriterIds[i * maxDSM], nmDsmCount);
+        }
+
         if(res3 != UA_STATUSCODE_GOOD)
             UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
                            "PubSub Publish: Sending a NetworkMessage failed");

+ 13 - 1
src/pubsub/ua_pubsub_networkmessage.c

@@ -810,6 +810,14 @@ UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p) {
     if(p->securityHeader.securityFooterEnabled && (p->securityHeader.securityFooterSize > 0))
         UA_ByteString_deleteMembers(&p->securityFooter);
 
+    if(p->messageIdEnabled){
+           UA_String_deleteMembers(&p->messageId);
+    }
+
+    if(p->publisherIdEnabled && p->publisherIdType == UA_PUBLISHERDATATYPE_STRING){
+       UA_String_deleteMembers(&p->publisherId.publisherIdString);
+    }
+
     memset(p, 0, sizeof(UA_NetworkMessage));
 }
 
@@ -1264,6 +1272,11 @@ void UA_DataSetMessage_free(const UA_DataSetMessage* p) {
         if(p->data.keyFrameData.dataSetFields != NULL)
             UA_Array_delete(p->data.keyFrameData.dataSetFields, p->data.keyFrameData.fieldCount,
                             &UA_TYPES[UA_TYPES_DATAVALUE]);
+        /* Json keys */
+        if(p->data.keyFrameData.fieldNames != NULL){
+            UA_Array_delete(p->data.keyFrameData.fieldNames, p->data.keyFrameData.fieldCount,
+                            &UA_TYPES[UA_TYPES_STRING]);
+        }
     } else if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) {
         if(p->data.deltaFrameData.deltaFrameFields != NULL) {
             for(UA_UInt16 i = 0; i < p->data.deltaFrameData.fieldCount; i++) {
@@ -1277,5 +1290,4 @@ void UA_DataSetMessage_free(const UA_DataSetMessage* p) {
         }
     }
 }
-
 #endif /* UA_ENABLE_PUBSUB */

+ 21 - 0
src/pubsub/ua_pubsub_networkmessage.h

@@ -71,6 +71,8 @@ UA_DataSetMessageHeader_calcSizeBinary(const UA_DataSetMessageHeader* p);
 typedef struct {
     UA_UInt16 fieldCount;
     UA_DataValue* dataSetFields;
+    /* Json keys for the dataSetFields: TODO: own dataSetMessageType for json? */
+    UA_String* fieldNames;
 } UA_DataSetMessage_DataKeyFrameData;
 
 typedef struct {
@@ -156,6 +158,8 @@ typedef struct {
  * ^^^^^^^^^^^^^^^^^ */
 typedef struct {
     UA_Byte version;
+    UA_Boolean messageIdEnabled;
+    UA_String messageId; /* For Json NetworkMessage */
     UA_Boolean publisherIdEnabled;
     UA_Boolean groupHeaderEnabled;
     UA_Boolean payloadHeaderEnabled;
@@ -217,6 +221,23 @@ UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p);
 void
 UA_NetworkMessage_delete(UA_NetworkMessage* p);
 
+
+#ifdef UA_ENABLE_JSON_ENCODING
+UA_StatusCode
+UA_NetworkMessage_encodeJson(const UA_NetworkMessage *src,
+                             UA_Byte **bufPos, const UA_Byte **bufEnd, UA_String *namespaces,
+                             size_t namespaceSize, UA_String *serverUris,
+                             size_t serverUriSize, UA_Boolean useReversible);
+
+size_t
+UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src,
+                               UA_String *namespaces, size_t namespaceSize,
+                               UA_String *serverUris, size_t serverUriSize,
+                               UA_Boolean useReversible);
+
+UA_StatusCode UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, UA_ByteString *src);
+#endif
+
 _UA_END_DECLS
 
 #endif /* UA_PUBSUB_NETWORKMESSAGE_H_ */

+ 535 - 0
src/pubsub/ua_pubsub_networkmessage_json.c

@@ -0,0 +1,535 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2019 Fraunhofer IOSB (Author: Lukas Meling)
+ */
+
+#include "ua_types.h"
+#include "ua_types_generated_handling.h"
+#include "ua_types_encoding_json.h"
+#include "ua_pubsub_networkmessage.h"
+
+/* Json keys for dsm */
+const char * UA_DECODEKEY_MESSAGES = ("Messages");
+const char * UA_DECODEKEY_MESSAGETYPE = ("MessageType");
+const char * UA_DECODEKEY_MESSAGEID = ("MessageId");
+const char * UA_DECODEKEY_PUBLISHERID = ("PublisherId");
+const char * UA_DECODEKEY_DATASETCLASSID = ("DataSetClassId");
+
+/* Json keys for dsm */
+const char * UA_DECODEKEY_DATASETWRITERID = ("DataSetWriterId");
+const char * UA_DECODEKEY_SEQUENCENUMBER = ("SequenceNumber");
+const char * UA_DECODEKEY_METADATAVERSION = ("MetaDataVersion");
+const char * UA_DECODEKEY_TIMESTAMP = ("Timestamp");
+const char * UA_DECODEKEY_DSM_STATUS = ("Status");
+const char * UA_DECODEKEY_PAYLOAD = ("Payload");
+const char * UA_DECODEKEY_DS_TYPE = ("Type");
+
+/* -- json encoding/decoding -- */
+static UA_StatusCode writeJsonKey_UA_String(CtxJson *ctx, UA_String *in){
+    UA_STACKARRAY(char, out, in->length + 1);
+    memcpy(out, in->data, in->length);
+    out[in->length] = 0;
+    return writeJsonKey(ctx, out);
+}
+
+static UA_StatusCode
+UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src, UA_UInt16 dataSetWriterId, CtxJson *ctx){
+    status rv = writeJsonObjStart(ctx);
+
+    /* DataSetWriterId */
+    rv |= writeJsonKey(ctx, UA_DECODEKEY_DATASETWRITERID);
+    rv |= encodeJsonInternal(&dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16], ctx);
+    if(rv != UA_STATUSCODE_GOOD)
+        return rv;
+
+    /* DataSetMessageSequenceNr */
+    if(src->header.dataSetMessageSequenceNrEnabled) {
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_SEQUENCENUMBER);
+        rv |= encodeJsonInternal(&(src->header.dataSetMessageSequenceNr), &UA_TYPES[UA_TYPES_UINT16], ctx);
+        if(rv != UA_STATUSCODE_GOOD)
+            return rv;
+    }
+
+    /* MetaDataVersion */
+    if(src->header.configVersionMajorVersionEnabled || src->header.configVersionMinorVersionEnabled) {
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_METADATAVERSION);
+        UA_ConfigurationVersionDataType cvd;
+        cvd.majorVersion = src->header.configVersionMajorVersion;
+        cvd.minorVersion = src->header.configVersionMinorVersion;
+        rv |= encodeJsonInternal(&cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE], ctx);
+        if(rv != UA_STATUSCODE_GOOD)
+            return rv;
+    }
+
+    /* Timestamp */
+    if(src->header.timestampEnabled) {
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_TIMESTAMP);
+        rv |= encodeJsonInternal(&(src->header.timestamp), &UA_TYPES[UA_TYPES_DATETIME], ctx);
+        if(rv != UA_STATUSCODE_GOOD)
+            return rv;
+    }
+
+    /* Status */
+    if(src->header.statusEnabled) {
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_DSM_STATUS);
+        rv |= encodeJsonInternal(&(src->header.status), &UA_TYPES[UA_TYPES_STATUSCODE], ctx);
+        if(rv != UA_STATUSCODE_GOOD)
+            return rv;
+    }
+
+    rv |= writeJsonKey(ctx, UA_DECODEKEY_PAYLOAD);
+    rv |= writeJsonObjStart(ctx);
+
+    /* TODO: currently no difference between delta and key frames. Own dataSetMessageType for json?*/
+    if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) {
+        if(src->data.keyFrameData.fieldNames == NULL){
+            return UA_STATUSCODE_BADENCODINGERROR;
+        }
+
+        if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
+            /* KEYFRAME VARIANT */
+            for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
+                rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
+                rv |= encodeJsonInternal(&(src->data.keyFrameData.dataSetFields[i].value),
+                        &UA_TYPES[UA_TYPES_VARIANT], ctx);
+                if(rv != UA_STATUSCODE_GOOD)
+                    return rv;
+            }
+        } else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) {
+            return UA_STATUSCODE_BADNOTIMPLEMENTED;
+        } else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
+            /* KEYFRAME DATAVALUE */
+            for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
+                rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
+                rv |= encodeJsonInternal(&(src->data.keyFrameData.dataSetFields[i]), &UA_TYPES[UA_TYPES_DATAVALUE], ctx);
+                if(rv != UA_STATUSCODE_GOOD)
+                    return rv;
+            }
+        }
+    }else if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME){
+        return UA_STATUSCODE_BADNOTSUPPORTED;
+    }
+    rv |= writeJsonObjEnd(ctx); /* Payload */
+    rv |= writeJsonObjEnd(ctx); /* DataSetMessage */
+    return rv;
+}
+
+static UA_StatusCode
+UA_NetworkMessage_encodeJson_internal(const UA_NetworkMessage* src, CtxJson *ctx) {
+    status rv = UA_STATUSCODE_GOOD;
+    /* currently only ua-data is supported */
+    if(src->networkMessageType == UA_NETWORKMESSAGE_DATASET) {
+        writeJsonObjStart(ctx);
+
+        /* Table 91 – JSON NetworkMessage Definition
+            MessageId | String | A globally unique identifier for the message.
+            This value is mandatory. */
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_MESSAGEID);
+        /* if a messageId is given use it, otherwise generate a GUID(-string). MessageId is always decoded as a string. */
+        if(src->messageIdEnabled){
+            rv |= encodeJsonInternal(&src->messageId, &UA_TYPES[UA_TYPES_STRING], ctx);
+        }else{
+            UA_Guid guid = UA_Guid_random();
+            rv |= encodeJsonInternal(&guid, &UA_TYPES[UA_TYPES_GUID], ctx);
+        }
+
+        /* MessageType */
+        rv |= writeJsonKey(ctx, UA_DECODEKEY_MESSAGETYPE);
+        UA_String s = UA_STRING("ua-data");
+        rv |= encodeJsonInternal(&s, &UA_TYPES[UA_TYPES_STRING], ctx);
+
+        /* PublisherId */
+        if(src->publisherIdEnabled) {
+            rv = writeJsonKey(ctx, UA_DECODEKEY_PUBLISHERID);
+            switch (src->publisherIdType) {
+            case UA_PUBLISHERDATATYPE_BYTE:
+                rv |= encodeJsonInternal(&src->publisherId.publisherIdByte, &UA_TYPES[UA_TYPES_BYTE], ctx);
+                break;
+
+            case UA_PUBLISHERDATATYPE_UINT16:
+                rv |= encodeJsonInternal(&src->publisherId.publisherIdUInt16, &UA_TYPES[UA_TYPES_UINT16], ctx);
+                break;
+
+            case UA_PUBLISHERDATATYPE_UINT32:
+                rv |= encodeJsonInternal(&src->publisherId.publisherIdUInt32, &UA_TYPES[UA_TYPES_UINT32], ctx);
+                break;
+
+            case UA_PUBLISHERDATATYPE_UINT64:
+                rv |= encodeJsonInternal(&src->publisherId.publisherIdUInt64, &UA_TYPES[UA_TYPES_UINT64], ctx);
+                break;
+
+            case UA_PUBLISHERDATATYPE_STRING:
+                rv |= encodeJsonInternal(&src->publisherId.publisherIdString, &UA_TYPES[UA_TYPES_STRING], ctx);
+                break;
+            }
+        }
+        if(rv != UA_STATUSCODE_GOOD)
+            return rv;
+
+        /* DataSetClassId */
+        if(src->dataSetClassIdEnabled) {
+            rv |= writeJsonKey(ctx, UA_DECODEKEY_DATASETCLASSID);
+            rv |= encodeJsonInternal(&src->dataSetClassId, &UA_TYPES[UA_TYPES_GUID], ctx);
+            if(rv != UA_STATUSCODE_GOOD)
+                return rv;
+        }
+
+        /* Payload: DataSetMessages */
+        UA_Byte count = src->payloadHeader.dataSetPayloadHeader.count;
+        if(count > 0){
+            UA_UInt16 *dataSetWriterIds = src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds;
+            if(!dataSetWriterIds){
+                return UA_STATUSCODE_BADENCODINGERROR;
+            }
+
+            rv |= writeJsonKey(ctx, UA_DECODEKEY_MESSAGES);
+            rv |= writeJsonArrStart(ctx); /* start array */
+
+            for (UA_UInt16 i = 0; i < count; i++) {
+                writeJsonCommaIfNeeded(ctx);
+                rv |= UA_DataSetMessage_encodeJson_internal(&(src->payload.dataSetPayload.dataSetMessages[i]), dataSetWriterIds[i], ctx);
+                if(rv != UA_STATUSCODE_GOOD)
+                    return rv;
+                /* comma is needed if more dsm are present */
+                ctx->commaNeeded[ctx->depth] = true;
+            }
+            rv |= writeJsonArrEnd(ctx); /* end array */
+        }
+        rv |= writeJsonObjEnd(ctx);
+    } else {
+        /* no discovery message implemented */
+        rv = UA_STATUSCODE_BADNOTIMPLEMENTED;
+    }
+    return rv;
+}
+
+UA_StatusCode
+UA_NetworkMessage_encodeJson(const UA_NetworkMessage *src,
+                             UA_Byte **bufPos, const UA_Byte **bufEnd, UA_String *namespaces,
+                             size_t namespaceSize, UA_String *serverUris,
+                             size_t serverUriSize, UA_Boolean useReversible) {
+    /* Set up the context */
+    CtxJson ctx;
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.pos = *bufPos;
+    ctx.end = *bufEnd;
+    ctx.depth = 0;
+    ctx.namespaces = namespaces;
+    ctx.namespacesSize = namespaceSize;
+    ctx.serverUris = serverUris;
+    ctx.serverUrisSize = serverUriSize;
+    ctx.useReversible = useReversible;
+    ctx.calcOnly = false;
+
+    status ret = UA_NetworkMessage_encodeJson_internal(src, &ctx);
+
+    *bufPos = ctx.pos;
+    *bufEnd = ctx.end;
+    return ret;
+}
+
+size_t
+UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src,
+                               UA_String *namespaces, size_t namespaceSize,
+                               UA_String *serverUris, size_t serverUriSize,
+                               UA_Boolean useReversible){
+
+    /* Set up the context */
+    CtxJson ctx;
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.pos = 0;
+    ctx.end = (const UA_Byte*)(uintptr_t)SIZE_MAX;
+    ctx.depth = 0;
+    ctx.namespaces = namespaces;
+    ctx.namespacesSize = namespaceSize;
+    ctx.serverUris = serverUris;
+    ctx.serverUrisSize = serverUriSize;
+    ctx.useReversible = useReversible;
+    ctx.calcOnly = true;
+
+    status ret = UA_NetworkMessage_encodeJson_internal(src, &ctx);
+    if(ret != UA_STATUSCODE_GOOD)
+        return 0;
+    return (size_t)ctx.pos;
+}
+
+/* decode  json */
+static status MetaDataVersion_decodeJsonInternal(void* cvd, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken){
+    return decodeJsonInternal(cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE], ctx, parseCtx, UA_TRUE);
+}
+
+static status DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken){
+    UA_DataSetMessage* dsm = (UA_DataSetMessage*)dsmP;
+    dsm->header.dataSetMessageValid = UA_TRUE;
+    if(isJsonNull(ctx, parseCtx)){
+        parseCtx->index++;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    size_t length = (size_t)parseCtx->tokenArray[parseCtx->index].size;
+    UA_String *fieldNames = (UA_String*)UA_calloc(length, sizeof(UA_String));
+    dsm->data.keyFrameData.fieldNames = fieldNames;
+    dsm->data.keyFrameData.fieldCount = (UA_UInt16)length;
+    dsm->data.keyFrameData.dataSetFields =
+                    (UA_DataValue *)UA_Array_new(dsm->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]);
+
+    status ret = UA_STATUSCODE_GOOD;
+    parseCtx->index++; // We go to first Object key!
+
+    /* iterate over the key/value pairs in the object. Keys are stored in fieldnames. */
+    for(size_t i = 0; i < length; ++i) {
+        ret = getDecodeSignature(UA_TYPES_STRING)(&fieldNames[i], type, ctx, parseCtx, UA_TRUE);
+        if(ret != UA_STATUSCODE_GOOD){
+            return ret;
+        }
+
+        //TODO: Is field value a variant or datavalue? Current check if type and body present.
+        UA_Boolean isVariant = UA_TRUE;
+        size_t searchResult = 0;
+        status foundType = lookAheadForKey("Type", ctx, parseCtx, &searchResult);
+        status foundBody = lookAheadForKey("Body", ctx, parseCtx, &searchResult);
+        if(foundType == UA_STATUSCODE_GOOD && foundBody == UA_STATUSCODE_GOOD){
+            dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
+        }else{
+            isVariant = UA_FALSE;
+            dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
+        }
+
+        UA_DataValue_init(&dsm->data.keyFrameData.dataSetFields[i]);
+
+        if(isVariant){
+            ret = getDecodeSignature(UA_TYPES_VARIANT)(&dsm->data.keyFrameData.dataSetFields[i].value, type, ctx, parseCtx, UA_TRUE);
+            dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
+        }else{
+            ret = getDecodeSignature(UA_TYPES_DATAVALUE)(&dsm->data.keyFrameData.dataSetFields[i], type, ctx, parseCtx, UA_TRUE);
+            dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
+        }
+
+        if(ret != UA_STATUSCODE_GOOD){
+            return ret;
+        }
+    }
+    return ret;
+}
+
+static status
+DatasetMessage_Payload_decodeJsonInternal(UA_DataSetMessage* dsm, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
+    UA_ConfigurationVersionDataType cvd;
+    UA_UInt16 dataSetWriterId; /* the id is currently not processed */
+
+    dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
+
+    DecodeEntry entries[6] = {
+        {UA_DECODEKEY_DATASETWRITERID, &dataSetWriterId, getDecodeSignature(UA_TYPES_UINT16), false},
+        {UA_DECODEKEY_SEQUENCENUMBER, &dsm->header.dataSetMessageSequenceNr, getDecodeSignature(UA_TYPES_UINT16), false},
+        {UA_DECODEKEY_METADATAVERSION, &cvd, &MetaDataVersion_decodeJsonInternal, false},
+        {UA_DECODEKEY_TIMESTAMP, &dsm->header.timestamp, getDecodeSignature(UA_TYPES_DATETIME), false},
+        {UA_DECODEKEY_DSM_STATUS, &dsm->header.status, getDecodeSignature(UA_TYPES_UINT16), false},
+        {UA_DECODEKEY_PAYLOAD, dsm, &DataSetPayload_decodeJsonInternal, false}
+    };
+
+    status ret = decodeFields(ctx, parseCtx, entries, 6, NULL);
+    if(ret != UA_STATUSCODE_GOOD || !entries[0].found){
+        /* no dataSetwriterid. Is mandatory. Abort. */
+        return UA_STATUSCODE_BADDECODINGERROR;
+    }else{
+        if(parseCtx->custom != NULL){
+            UA_UInt16* dataSetWriterIdsArray = (UA_UInt16*)parseCtx->custom;
+
+            if(*parseCtx->currentCustomIndex  < parseCtx->numCustom){
+                 dataSetWriterIdsArray[*parseCtx->currentCustomIndex] = dataSetWriterId;
+                 (*parseCtx->currentCustomIndex)++;
+            }else{
+                return UA_STATUSCODE_BADDECODINGERROR;
+            }
+        }else{
+            return UA_STATUSCODE_BADDECODINGERROR;
+        }
+    }
+    dsm->header.dataSetMessageSequenceNrEnabled = entries[1].found;
+    dsm->header.configVersionMajorVersion = cvd.majorVersion;
+    dsm->header.configVersionMinorVersion = cvd.minorVersion;
+    dsm->header.configVersionMajorVersionEnabled = entries[2].found;
+    dsm->header.configVersionMinorVersionEnabled = entries[2].found;
+    dsm->header.timestampEnabled = entries[3].found;
+    dsm->header.statusEnabled = entries[4].found;
+    if(!entries[5].found){
+        /* No payload found */
+        return UA_STATUSCODE_BADDECODINGERROR;
+    }
+
+    dsm->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
+    dsm->header.picoSecondsIncluded = UA_FALSE;
+    dsm->header.dataSetMessageValid = UA_TRUE;
+    dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
+    return ret;
+}
+
+static status
+DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
+    /* Array! */
+    if(getJsmnType(parseCtx) != JSMN_ARRAY){
+        return UA_STATUSCODE_BADDECODINGERROR;
+    }
+    size_t length = (size_t)parseCtx->tokenArray[parseCtx->index].size;
+
+    /* Return early for empty arrays */
+    if(length == 0) {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* Allocate memory */
+    UA_DataSetMessage *dsm = (UA_DataSetMessage*)UA_calloc(length, sizeof(UA_DataSetMessage));
+    if(dsm == NULL)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    /* Copy new Pointer do dest */
+    memcpy(dst, &dsm, sizeof(void*));
+
+    /* We go to first Array member! */
+    parseCtx->index++;
+
+    status ret = UA_STATUSCODE_BADDECODINGERROR;
+    /* Decode array members */
+    for(size_t i = 0; i < length; ++i) {
+        ret = DatasetMessage_Payload_decodeJsonInternal(&dsm[i], NULL, ctx, parseCtx, UA_TRUE);
+        if(ret != UA_STATUSCODE_GOOD){
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+static status NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson *ctx, ParseCtx *parseCtx){
+    memset(dst, 0, sizeof(UA_NetworkMessage));
+    dst->chunkMessage = UA_FALSE;
+    dst->groupHeaderEnabled = UA_FALSE;
+    dst->payloadHeaderEnabled = UA_FALSE;
+    dst->picosecondsEnabled = UA_FALSE;
+    dst->promotedFieldsEnabled = UA_FALSE;
+
+    /* Look forward for publisheId, if present check if type if primitve (Number) or String. */
+    u8 publishIdTypeIndex = UA_TYPES_STRING;
+    {
+        size_t searchResultPublishIdType = 0;
+        status found = lookAheadForKey(UA_DECODEKEY_PUBLISHERID, ctx, parseCtx, &searchResultPublishIdType);
+        if(found == UA_STATUSCODE_GOOD){
+            jsmntok_t publishIdToken = parseCtx->tokenArray[searchResultPublishIdType];
+            if(publishIdToken.type == JSMN_PRIMITIVE){
+                publishIdTypeIndex = UA_TYPES_UINT64;
+                dst->publisherIdType = UA_PUBLISHERDATATYPE_UINT64; //store in biggest possible
+            }else if(publishIdToken.type == JSMN_STRING){
+                publishIdTypeIndex = UA_TYPES_STRING;
+                dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
+            }else{
+                return UA_STATUSCODE_BADDECODINGERROR;
+            }
+        }
+    }
+
+    size_t messageCount = 0;
+    {
+        //Is Messages an Array? How big?
+        size_t searchResultMessages = 0;
+        status found = lookAheadForKey(UA_DECODEKEY_MESSAGES, ctx, parseCtx, &searchResultMessages);
+        if(found == UA_STATUSCODE_GOOD){
+            jsmntok_t bodyToken = parseCtx->tokenArray[searchResultMessages];
+            if(bodyToken.type == JSMN_ARRAY){
+                messageCount = (size_t)parseCtx->tokenArray[searchResultMessages].size;
+            }else{
+                return UA_STATUSCODE_BADNOTIMPLEMENTED;
+            }
+        }else{
+            return UA_STATUSCODE_BADNOTIMPLEMENTED;
+        }
+    }
+
+    //Set up custom context for the dataSetwriterId
+    size_t currentCustomIndex = 0;
+    parseCtx->custom = (void*)UA_calloc(messageCount, sizeof(UA_UInt16));
+    parseCtx->currentCustomIndex = &currentCustomIndex;
+    parseCtx->numCustom = messageCount;
+
+    /* MessageType */
+    UA_Boolean isUaData = UA_TRUE;
+    size_t searchResultMessageType = 0;
+    status found = lookAheadForKey(UA_DECODEKEY_MESSAGETYPE, ctx, parseCtx, &searchResultMessageType);
+    if(found == UA_STATUSCODE_GOOD){
+        size_t size = (size_t)(parseCtx->tokenArray[searchResultMessageType].end - parseCtx->tokenArray[searchResultMessageType].start);
+        char* msgType = (char*)(ctx->pos + parseCtx->tokenArray[searchResultMessageType].start);
+        if(size == 7){ //ua-data
+            if(strncmp(msgType, "ua-data", size) != 0){
+                return UA_STATUSCODE_BADDECODINGERROR;
+            }else{
+                isUaData = UA_TRUE;
+            }
+        }else if(size == 11){ //ua-metadata
+            if(strncmp(msgType, "ua-metadata", size) != 0){
+                return UA_STATUSCODE_BADDECODINGERROR;
+            }else{
+                isUaData = UA_FALSE;
+            }
+        }else{
+            return UA_STATUSCODE_BADDECODINGERROR;
+        }
+    }else{
+        return UA_STATUSCODE_BADDECODINGERROR;
+    }
+
+    if(isUaData){
+        /* Network Message */
+        UA_String messageType;
+        DecodeEntry entries[5] = {
+            {UA_DECODEKEY_MESSAGEID, &dst->messageId, getDecodeSignature(UA_TYPES_STRING), false},
+            {UA_DECODEKEY_MESSAGETYPE, &messageType, NULL, false},
+            {UA_DECODEKEY_PUBLISHERID, &dst->publisherId.publisherIdString, getDecodeSignature(publishIdTypeIndex), false},
+            {UA_DECODEKEY_DATASETCLASSID, &dst->dataSetClassId, getDecodeSignature(UA_TYPES_GUID), false},
+            {UA_DECODEKEY_MESSAGES, &dst->payload.dataSetPayload.dataSetMessages, &DatasetMessage_Array_decodeJsonInternal, false}
+        };
+
+        //Store publisherId in correct union
+        if(publishIdTypeIndex == UA_TYPES_UINT64)
+            entries[2].fieldPointer = &dst->publisherId.publisherIdUInt64;
+
+        status ret = decodeFields(ctx, parseCtx, entries, 5, NULL);
+        if(ret != UA_STATUSCODE_GOOD){
+            return ret;
+        }
+
+        dst->messageIdEnabled = entries[0].found;
+        dst->publisherIdEnabled = entries[2].found;
+        if(dst->publisherIdEnabled){
+            dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
+        }
+        dst->dataSetClassIdEnabled = entries[3].found;
+        dst->payloadHeaderEnabled = UA_TRUE;
+        dst->payloadHeader.dataSetPayloadHeader.count = (UA_Byte)messageCount;
+
+        //Set the dataSetWriterIds. They are filled in the dataSet decoding.
+        dst->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16*)parseCtx->custom;
+        return ret;
+    }else{
+        //TODO: MetaData
+        return UA_STATUSCODE_BADNOTIMPLEMENTED;
+    }
+}
+
+status UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, UA_ByteString *src){
+    /* Set up the context */
+    CtxJson ctx;
+    memset(&ctx, 0, sizeof(CtxJson));
+    ParseCtx parseCtx;
+    memset(&parseCtx, 0, sizeof(ParseCtx));
+    parseCtx.tokenArray = (jsmntok_t*)malloc(sizeof(jsmntok_t) * TOKENCOUNT);
+    memset(parseCtx.tokenArray, 0, sizeof(jsmntok_t) * TOKENCOUNT);
+    status ret = tokenize(&parseCtx, &ctx, src);
+    if(ret != UA_STATUSCODE_GOOD){
+        return ret;
+    }
+    ret = NetworkMessage_decodeJsonInternal(dst, &ctx, &parseCtx);
+    free(parseCtx.tokenArray);
+    return ret;
+}

+ 11 - 3
src/ua_types_encoding_json.c

@@ -84,7 +84,6 @@ extern const encodeJsonSignature encodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1]
 extern const decodeJsonSignature decodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
 
 /* Forward declarations */
-static status encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx);
 UA_String UA_DateTime_toJSON(UA_DateTime t);
 ENCODE_JSON(ByteString);
 
@@ -146,6 +145,16 @@ writeJsonArrElm(CtxJson *ctx, const void *value,
     return ret;
 }
 
+status writeJsonObjElm(CtxJson *ctx, UA_String *key,
+                       const void *value, const UA_DataType *type){
+    UA_STACKARRAY(char, out, key->length + 1);
+    memcpy(out, key->data, key->length);
+    out[key->length] = 0;
+    status ret = writeJsonKey(ctx, out);
+    ret |= encodeJsonInternal(value, type, ctx);
+    return ret;
+}
+
 status writeJsonNull(CtxJson *ctx) {
     if(ctx->pos + 4 > ctx->end)
         return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
@@ -1405,7 +1414,7 @@ const encodeJsonSignature encodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
     (encodeJsonSignature) encodeJsonInternal,
 };
 
-static status
+status
 encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx) {
     /* Check the recursion limit */
     if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
@@ -1480,7 +1489,6 @@ UA_encodeJson(const void *src, const UA_DataType *type,
 /************/
 /* CalcSize */
 /************/
-
 size_t
 UA_calcSizeJson(const void *src, const UA_DataType *type,
                 UA_String *namespaces, size_t namespaceSize,

+ 9 - 0
src/ua_types_encoding_json.h

@@ -112,10 +112,19 @@ calcJsonArrEnd(CtxJson *ctx) {
     return writeJsonArrEnd(ctx);
 }
 
+status
+encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx);
+
 typedef struct {
     jsmntok_t *tokenArray;
     UA_Int32 tokenCount;
     UA_UInt16 index;
+
+    /* Additonal data for special cases such as networkmessage/datasetmessage
+     * Currently only used for dataSetWriterIds */
+    size_t numCustom;
+    void * custom;
+    size_t* currentCustomIndex;
 } ParseCtx;
 
 typedef UA_StatusCode

+ 7 - 4
tests/CMakeLists.txt

@@ -109,10 +109,13 @@ if(UA_ENABLE_JSON_ENCODING)
     add_test_valgrind(types_builtin_json ${TESTS_BINARY_DIR}/check_types_builtin_json)
 
     if(UA_ENABLE_PUBSUB)
-        #later batch
-        #add_executable(check_pubsub_encoding_json pubsub/check_pubsub_encoding_json.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
-        #target_link_libraries(check_pubsub_encoding_json ${LIBS})
-        #add_test_valgrind(pubsub_encoding_json ${TESTS_BINARY_DIR}/check_pubsub_encoding_json)
+        add_executable(check_pubsub_encoding_json pubsub/check_pubsub_encoding_json.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+        target_link_libraries(check_pubsub_encoding_json ${LIBS})
+        add_test_valgrind(pubsub_encoding_json ${TESTS_BINARY_DIR}/check_pubsub_encoding_json)
+
+        add_executable(check_pubsub_publish_json pubsub/check_pubsub_publish_json.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-plugins>)
+        target_link_libraries(check_pubsub_publish_json ${LIBS})
+        add_test_valgrind(pubsub_publish_json ${TESTS_BINARY_DIR}/check_pubsub_publish_json)
     endif()
 endif()
 

+ 409 - 0
tests/pubsub/check_pubsub_encoding_json.c

@@ -0,0 +1,409 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2018 Fraunhofer IOSB (Author: Lukas Meling)
+ */
+
+#include "ua_types.h"
+#include "ua_client.h"
+#include "ua_util.h"
+#include "ua_pubsub_networkmessage.h"
+#include "check.h"
+
+START_TEST(UA_PubSub_EncodeAllOptionalFields) {
+    UA_NetworkMessage m;
+    memset(&m, 0, sizeof(UA_NetworkMessage));
+    m.version = 1;
+    m.networkMessageType = UA_NETWORKMESSAGE_DATASET;
+    m.payloadHeaderEnabled = true;
+    m.payloadHeader.dataSetPayloadHeader.count = 1;
+    UA_UInt16 dsWriter1 = 12345;
+    m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16 *)UA_Array_new(m.payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]);
+    m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0] = dsWriter1;
+
+    size_t memsize = m.payloadHeader.dataSetPayloadHeader.count * sizeof(UA_DataSetMessage);
+    m.payload.dataSetPayload.dataSetMessages = (UA_DataSetMessage*)UA_malloc(memsize);
+    memset(m.payload.dataSetPayload.dataSetMessages, 0, memsize);
+
+    /* enable messageId */
+    m.messageIdEnabled = true;
+    m.messageId = UA_STRING_ALLOC("ABCDEFGH");
+
+    /* enable publisherId */
+    m.publisherIdEnabled = true;
+    m.publisherIdType = UA_PUBLISHERDATATYPE_UINT16;
+    m.publisherId.publisherIdUInt16 = 65535;
+
+    /* enable dataSetClassId */
+    m.dataSetClassIdEnabled = true;
+    m.dataSetClassId.data1 = 1;
+    m.dataSetClassId.data2 = 2;
+    m.dataSetClassId.data3 = 3;
+
+    /* DatasetMessage */
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding = UA_FIELDENCODING_VARIANT;
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
+    UA_UInt16 fieldCountDS1 = 1;
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount = fieldCountDS1;
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields =
+        (UA_DataValue*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]);
+    UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0]);
+
+    /* enable DataSetMessageSequenceNr */
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNrEnabled = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr = 4711;
+
+    /* enable metaDataVersion */
+    m.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersionEnabled = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersionEnabled = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersion = 42;
+    m.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersion = 7;
+
+    /* enable timestamp */
+    m.payload.dataSetPayload.dataSetMessages[0].header.timestampEnabled = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.timestamp = 11111111111111;
+
+    /* enable status */
+    m.payload.dataSetPayload.dataSetMessages[0].header.statusEnabled = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.status = 12345;
+
+    /* Set fieldnames */
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames =
+        (UA_String*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]);
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0] = UA_STRING_ALLOC("Field1");
+
+    UA_UInt32 iv = 27;
+    UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value, &iv, &UA_TYPES[UA_TYPES_UINT32]);
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue = true;
+
+    size_t size = UA_NetworkMessage_calcSizeJson(&m, NULL, 0, NULL, 0, true);
+    ck_assert(size == 338);
+
+    UA_ByteString buffer;
+    UA_StatusCode rv = UA_ByteString_allocBuffer(&buffer, size+1);
+    ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
+
+    UA_Byte *bufPos = buffer.data;
+    memset(bufPos, 0, size+1);
+    const UA_Byte *bufEnd = &(buffer.data[buffer.length]);
+
+
+    rv = UA_NetworkMessage_encodeJson(&m, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
+    *bufPos = 0;
+    // then
+    ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
+
+    char* result = "{\"MessageId\":\"ABCDEFGH\",\"MessageType\":\"ua-data\",\"PublisherId\":65535,\"DataSetClassId\":\"00000001-0002-0003-0000-000000000000\",\"Messages\":[{\"DataSetWriterId\":12345,\"SequenceNumber\":4711,\"MetaDataVersion\":{\"MajorVersion\":42,\"MinorVersion\":7},\"Timestamp\":\"1601-01-13T20:38:32.111Z\",\"Status\":2764857,\"Payload\":{\"Field1\":{\"Type\":7,\"Body\":27}}}]}";
+    ck_assert_str_eq(result, (char*)buffer.data);
+
+    UA_ByteString_deleteMembers(&buffer);
+    UA_NetworkMessage_deleteMembers(&m);
+}
+END_TEST
+
+START_TEST(UA_PubSub_EnDecode) {
+    UA_NetworkMessage m;
+    memset(&m, 0, sizeof(UA_NetworkMessage));
+    m.version = 1;
+    m.networkMessageType = UA_NETWORKMESSAGE_DATASET;
+    m.payloadHeaderEnabled = true;
+    m.payloadHeader.dataSetPayloadHeader.count = 2;
+    UA_UInt16 dsWriter1 = 4;
+    UA_UInt16 dsWriter2 = 7;
+    m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16 *)UA_Array_new(m.payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]);
+    m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0] = dsWriter1;
+    m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[1] = dsWriter2;
+
+    size_t memsize = m.payloadHeader.dataSetPayloadHeader.count * sizeof(UA_DataSetMessage);
+    m.payload.dataSetPayload.dataSetMessages = (UA_DataSetMessage*)UA_malloc(memsize);
+    memset(m.payload.dataSetPayload.dataSetMessages, 0, memsize);
+
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid = true;
+    m.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding = UA_FIELDENCODING_VARIANT;
+    m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
+    UA_UInt16 fieldCountDS1 = 1;
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount = fieldCountDS1;
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields =
+        (UA_DataValue*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]);
+    UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0]);
+
+    /* Set fieldnames */
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames =
+        (UA_String*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]);
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0] = UA_STRING_ALLOC("Field1");
+
+    UA_UInt32 iv = 27;
+    UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value, &iv, &UA_TYPES[UA_TYPES_UINT32]);
+    m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue = true;
+
+    m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid = true;
+    m.payload.dataSetPayload.dataSetMessages[1].header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
+    m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
+    UA_UInt16 fieldCountDS2 = 2;
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.fieldCount = fieldCountDS2;
+    memsize = sizeof(UA_DataSetMessage_DeltaFrameField) * m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldCount;
+    m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields = (UA_DataSetMessage_DeltaFrameField*)UA_malloc(memsize);
+     /* Set fieldnames */
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.fieldNames =
+        (UA_String*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]);
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.fieldNames[0] = UA_STRING_ALLOC("Field2.1");
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.fieldNames[1] = UA_STRING_ALLOC("Field2.2");
+
+
+    UA_Guid gv = UA_Guid_random();
+    UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[0]);
+    UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[0].value, &gv, &UA_TYPES[UA_TYPES_GUID]);
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[0].hasValue = true;
+
+    UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[1]);
+    UA_Int64 iv64 = 152478978534;
+    UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[1].value, &iv64, &UA_TYPES[UA_TYPES_INT64]);
+    m.payload.dataSetPayload.dataSetMessages[1].data.keyFrameData.dataSetFields[1].hasValue = true;
+
+    UA_StatusCode rv = UA_STATUSCODE_UNCERTAININITIALVALUE;
+
+    size_t size = UA_NetworkMessage_calcSizeJson(&m, NULL, 0, NULL, 0, true);
+    ck_assert(size == 318);
+
+    UA_ByteString buffer;
+    rv = UA_ByteString_allocBuffer(&buffer, size);
+    ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
+
+    UA_Byte *bufPos = buffer.data;
+    memset(bufPos, 0, size);
+    const UA_Byte *bufEnd = &(buffer.data[buffer.length]);
+
+
+    rv = UA_NetworkMessage_encodeJson(&m, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
+    //*bufPos = 0;
+    // then
+    ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
+
+    UA_NetworkMessage m2;
+    memset(&m2, 0, sizeof(UA_NetworkMessage));
+    rv = UA_NetworkMessage_decodeJson(&m2, &buffer);
+    ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
+    ck_assert(m.networkMessageType == m2.networkMessageType);
+    ck_assert(m.timestampEnabled == m2.timestampEnabled);
+    ck_assert(m.dataSetClassIdEnabled == m2.dataSetClassIdEnabled);
+    ck_assert(m.groupHeaderEnabled == m2.groupHeaderEnabled);
+    ck_assert(m.picosecondsEnabled == m2.picosecondsEnabled);
+    ck_assert(m.promotedFieldsEnabled == m2.promotedFieldsEnabled);
+    ck_assert(m.publisherIdEnabled == m2.publisherIdEnabled);
+    ck_assert(m.securityEnabled == m2.securityEnabled);
+    ck_assert(m.chunkMessage == m2.chunkMessage);
+    ck_assert(m.payloadHeaderEnabled == m2.payloadHeaderEnabled);
+    ck_assert_uint_eq(m2.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], dsWriter1);
+    ck_assert_uint_eq(m2.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[1], dsWriter2);
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid == m2.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid);
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding == m2.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding);
+    ck_assert_int_eq(m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, fieldCountDS1);
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue == m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue);
+    ck_assert_uint_eq((uintptr_t)m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.type, (uintptr_t)&UA_TYPES[UA_TYPES_UINT32]);
+    ck_assert_uint_eq(*(UA_UInt32 *)m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data, iv);
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasSourceTimestamp == m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasSourceTimestamp);
+
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid == m2.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid);
+    ck_assert(m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.hasSourceTimestamp == m2.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.hasSourceTimestamp);
+    UA_ByteString_deleteMembers(&buffer);
+    UA_NetworkMessage_deleteMembers(&m);
+    UA_NetworkMessage_deleteMembers(&m2);
+}
+END_TEST
+
+
+START_TEST(UA_NetworkMessage_oneMessage_twoFields_json_decode) {
+    // given
+    UA_NetworkMessage out;
+    UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\",\"MessageType\":\"ua-data\",\"Messages\":[{\"DataSetWriterId\":62541,\"MetaDataVersion\":{\"MajorVersion\":1478393530,\"MinorVersion\":12345},\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Server localtime\":{\"Type\":13,\"Body\":\"2018-06-05T05:58:36.000Z\"}}}]}");
+    // when
+    UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
+    // then
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+     //NetworkMessage
+    ck_assert_int_eq(out.chunkMessage, false);
+    ck_assert_int_eq(out.dataSetClassIdEnabled, false);
+    ck_assert_int_eq(out.groupHeaderEnabled, false);
+    ck_assert_int_eq(out.networkMessageType, UA_NETWORKMESSAGE_DATASET);
+    ck_assert_int_eq(out.picosecondsEnabled, false);
+    ck_assert_int_eq(out.promotedFieldsEnabled, false);
+    ck_assert_int_eq(out.securityEnabled, false);
+    ck_assert_int_eq(out.timestampEnabled, false);
+    ck_assert_int_eq(out.publisherIdEnabled, false);
+
+    ck_assert_int_eq(out.payloadHeaderEnabled, true);
+    ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.count, 1);
+    ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], 62541);
+
+    //dataSetMessage
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNrEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType, UA_DATASETMESSAGE_DATAKEYFRAME);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding, UA_FIELDENCODING_VARIANT);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.picoSecondsIncluded, false);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersionEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersionEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersion, 12345);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersion, 1478393530);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
+    //ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetWriterId, 62541);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue, 1);
+    ck_assert_int_eq(*((UA_UInt16*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data), 42);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].hasValue, 1);
+    UA_DateTime *dt = (UA_DateTime*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].value.data;
+    UA_DateTimeStruct dts = UA_DateTime_toStruct(*dt);
+    ck_assert_int_eq(dts.year, 2018);
+    ck_assert_int_eq(dts.month, 6);
+    ck_assert_int_eq(dts.day, 5);
+    ck_assert_int_eq(dts.hour, 5);
+    ck_assert_int_eq(dts.min, 58);
+    ck_assert_int_eq(dts.sec, 36);
+    ck_assert_int_eq(dts.milliSec, 0);
+    ck_assert_int_eq(dts.microSec, 0);
+    ck_assert_int_eq(dts.nanoSec, 0);
+
+    UA_NetworkMessage_deleteMembers(&out);
+}
+END_TEST
+
+
+START_TEST(UA_NetworkMessage_json_decode) {
+    // given
+    UA_NetworkMessage out;
+    memset(&out,0,sizeof(UA_NetworkMessage));
+    UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\",\"MessageType\":\"ua-data\",\"Messages\":[{\"MetaDataVersion\":{\"MajorVersion\": 47, \"MinorVersion\": 47},\"DataSetWriterId\":62541,\"Status\":22,\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Server localtime\":{\"Type\":1,\"Body\":true}}}]}");
+    // when
+    UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
+    // then
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+    //NetworkMessage
+    ck_assert_int_eq(out.chunkMessage, false);
+    ck_assert_int_eq(out.dataSetClassIdEnabled, false);
+    ck_assert_int_eq(out.groupHeaderEnabled, false);
+    ck_assert_int_eq(out.networkMessageType, UA_NETWORKMESSAGE_DATASET);
+    ck_assert_int_eq(out.picosecondsEnabled, false);
+    ck_assert_int_eq(out.promotedFieldsEnabled, false);
+    ck_assert_int_eq(out.securityEnabled, false);
+    ck_assert_int_eq(out.timestampEnabled, false);
+    ck_assert_int_eq(out.publisherIdEnabled, false);
+
+    ck_assert_int_eq(out.payloadHeaderEnabled, true);
+    ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.count, 1);
+    ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], 62541);
+
+    //dataSetMessage
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNrEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType, UA_DATASETMESSAGE_DATAKEYFRAME);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding, UA_FIELDENCODING_VARIANT);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.picoSecondsIncluded, false);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.statusEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.status, 22);
+
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersionEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersionEnabled, true);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersion, 47);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersion, 47);
+
+    //dataSetFields
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue, true);
+    ck_assert_int_eq(*((UA_UInt16*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data), 42);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].hasValue, true);
+    ck_assert_int_eq(*((UA_Boolean*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].value.data), 1);
+
+    UA_NetworkMessage_deleteMembers(&out);
+}
+END_TEST
+
+START_TEST(UA_Networkmessage_DataSetFieldsNull_json_decode) {
+    // given
+    UA_NetworkMessage out;
+    memset(&out, 0, sizeof(UA_NetworkMessage));
+    UA_ByteString buf = UA_STRING("{ \"MessageId\": \"32235546-05d9-4fd7-97df-ea3ff3408574\",  "
+            "\"MessageType\": \"ua-data\",  \"PublisherId\": \"MQTT-Localhost\",  "
+            "\"DataSetClassId\": \"00000005-cab9-4470-8f8a-2c1ead207e0e\",  \"Messages\": "
+            "[    {      \"DataSetWriterId\": 1,      \"SequenceNumber\": 224,     \"MetaDataVersion\": "
+            "{        \"MajorVersion\": 1,        \"MinorVersion\": 1      },\"Payload\":null}]}");
+    // when
+    UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
+    // then
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert_int_eq(out.dataSetClassId.data1, 5);
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages->header.dataSetMessageSequenceNr, 224);
+    ck_assert_ptr_eq(out.payload.dataSetPayload.dataSetMessages->data.keyFrameData.dataSetFields, NULL);
+
+    UA_NetworkMessage_deleteMembers(&out);
+}
+END_TEST
+
+
+START_TEST(UA_NetworkMessage_fieldNames_json_decode) {
+    // given
+    UA_NetworkMessage out;
+    UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\","
+            "\"MessageType\":\"ua-data\",\"Messages\":"
+            "[{\"DataSetWriterId\":62541,\"MetaDataVersion\":"
+            "{\"MajorVersion\":1478393530,\"MinorVersion\":12345},"
+            "\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Test2\":"
+            "{\"Type\":13,\"Body\":\"2018-06-05T05:58:36.000Z\"}}}]}");
+    // when
+    UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
+    // then
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+     //NetworkMessage
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[0], 'T');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[1], 'e');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[2], 's');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[3], 't');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[0], 'T');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[1], 'e');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[2], 's');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[3], 't');
+    ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[4], '2');
+
+    UA_NetworkMessage_deleteMembers(&out);
+}
+END_TEST
+
+static Suite *testSuite_networkmessage(void) {
+    Suite *s = suite_create("Built-in Data Types 62541-6 Json");
+    TCase *tc_json_networkmessage = tcase_create("networkmessage_json");
+
+
+    tcase_add_test(tc_json_networkmessage, UA_PubSub_EncodeAllOptionalFields);
+    tcase_add_test(tc_json_networkmessage, UA_PubSub_EnDecode);
+    tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_oneMessage_twoFields_json_decode);
+    tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_json_decode);
+    tcase_add_test(tc_json_networkmessage, UA_Networkmessage_DataSetFieldsNull_json_decode);
+    tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_fieldNames_json_decode);
+
+    suite_add_tcase(s, tc_json_networkmessage);
+    return s;
+}
+
+int main(void) {
+    int      number_failed = 0;
+    Suite   *s;
+    SRunner *sr;
+
+    s  = testSuite_networkmessage();
+    sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    number_failed += srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 99 - 0
tests/pubsub/check_pubsub_publish_json.c

@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright (c) 2017 - 2018 Fraunhofer IOSB (Author: Andreas Ebner)
+ */
+
+#include "ua_server_pubsub.h"
+#include "src_generated/ua_types_generated_encoding_binary.h"
+#include "ua_types.h"
+#include "ua_pubsub.h"
+#include "ua_config_default.h"
+#include "ua_network_pubsub_udp.h"
+#include "ua_server_internal.h"
+#include "check.h"
+#include "stdio.h"
+
+UA_Server *server = NULL;
+UA_ServerConfig *config = NULL;
+UA_NodeId connection1, writerGroup1, publishedDataSet1, dataSetWriter1;
+
+static void setup(void) {
+    config = UA_ServerConfig_new_default();
+    config->pubsubTransportLayers = (UA_PubSubTransportLayer *) UA_malloc(sizeof(UA_PubSubTransportLayer));
+    if(!config->pubsubTransportLayers) {
+        UA_ServerConfig_delete(config);
+    }
+    config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
+    config->pubsubTransportLayersSize++;
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+    UA_PubSubConnectionConfig connectionConfig;
+    memset(&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
+    connectionConfig.name = UA_STRING("UADP Connection");
+    UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL, UA_STRING("opc.udp://224.0.0.22:4840/")};
+    UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
+                         &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
+    connectionConfig.transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
+    UA_Server_addPubSubConnection(server, &connectionConfig, &connection1);
+}
+
+static void teardown(void) {
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+START_TEST(SinglePublishDataSetField){
+        UA_WriterGroupConfig writerGroupConfig;
+        memset(&writerGroupConfig, 0, sizeof(writerGroupConfig));
+
+        writerGroupConfig.messageSettings.encoding = UA_EXTENSIONOBJECT_DECODED;
+        writerGroupConfig.messageSettings.content.decoded.type = &UA_TYPES[UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE];
+        UA_JsonDataSetWriterMessageDataType d;
+        d.dataSetMessageContentMask = UA_JSONDATASETMESSAGECONTENTMASK_SEQUENCENUMBER;
+        writerGroupConfig.messageSettings.content.decoded.data = &d;
+
+        writerGroupConfig.name = UA_STRING("WriterGroup 1");
+        writerGroupConfig.publishingInterval = 10;
+        writerGroupConfig.encodingMimeType = UA_PUBSUB_ENCODING_JSON;
+        UA_Server_addWriterGroup(server, connection1, &writerGroupConfig, &writerGroup1);
+
+        UA_PublishedDataSetConfig pdsConfig;
+        memset(&pdsConfig, 0, sizeof(UA_PublishedDataSetConfig));
+        pdsConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS;
+        pdsConfig.name = UA_STRING("PublishedDataSet 1");
+        UA_Server_addPublishedDataSet(server, &pdsConfig, &publishedDataSet1);
+        UA_DataSetWriterConfig dataSetWriterConfig;
+        memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
+        dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");
+        UA_Server_addDataSetWriter(server, writerGroup1, publishedDataSet1, &dataSetWriterConfig, &dataSetWriter1);
+        UA_DataSetFieldConfig dataSetFieldConfig;
+        memset(&dataSetFieldConfig, 0, sizeof(UA_DataSetFieldConfig));
+        dataSetFieldConfig.dataSetFieldType = UA_PUBSUB_DATASETFIELD_VARIABLE;
+        dataSetFieldConfig.field.variable.fieldNameAlias = UA_STRING("Server localtime");
+        dataSetFieldConfig.field.variable.promotedField = UA_FALSE;
+        dataSetFieldConfig.field.variable.publishParameters.publishedVariable = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_LOCALTIME);
+        dataSetFieldConfig.field.variable.publishParameters.attributeId = UA_ATTRIBUTEID_VALUE;
+        UA_Server_addDataSetField(server, publishedDataSet1, &dataSetFieldConfig, NULL);
+
+        UA_WriterGroup *wg = UA_WriterGroup_findWGbyId(server, writerGroup1);
+        UA_WriterGroup_publishCallback(server, wg);
+    } END_TEST
+
+int main(void) {
+    TCase *tc_pubsub_publish = tcase_create("PubSub publish");
+    tcase_add_checked_fixture(tc_pubsub_publish, setup, teardown);
+    tcase_add_test(tc_pubsub_publish, SinglePublishDataSetField);
+
+    Suite *s = suite_create("PubSub publishing json via udp");
+    suite_add_tcase(s, tc_pubsub_publish);
+
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr,CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 3 - 0
tools/schema/datatypes_pubsub.txt

@@ -25,3 +25,6 @@ ReaderGroupDataType
 DataSetWriterDataType
 DataSetReaderDataType
 PubSubState
+JsonDataSetWriterMessageDataType
+JsonDataSetMessageContentMask
+JsonNetworkMessageContentMask