This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/develop by this push:
     new 0c5f2ae  plc4go: first draft of WriterBufferJsonBased.go
0c5f2ae is described below

commit 0c5f2ae2f678cd12ad1dd0daf7a8b8ada22712b5
Author: Sebastian Rühl <sru...@apache.org>
AuthorDate: Wed Apr 21 00:38:14 2021 +0200

    plc4go: first draft of WriterBufferJsonBased.go
    
    + refactored reader and writers
---
 .../resources/templates/go/model-template.go.ftlh  |  12 +-
 go.mod                                             |   2 +-
 go.sum                                             |   2 +
 .../plc4go/s7/readwrite/model/COTPPacket.go        |   8 +-
 plc4go/internal/plc4go/s7/s7Io_test.go             | 217 +++++++++++++++----
 plc4go/internal/plc4go/spi/utils/Buffer.go         |  85 ++++++++
 .../plc4go/spi/utils/ReadBufferXmlBased.go         |  55 ++---
 .../plc4go/spi/utils/WriteBufferBoxBased.go        |  52 ++---
 .../plc4go/spi/utils/WriteBufferJsonBased.go       | 232 +++++++++++++++++++++
 .../plc4go/spi/utils/WriteBufferXmlBased.go        | 106 +++-------
 plc4go/internal/plc4go/spi/utils/bufferCommons.go  | 103 +++++++++
 11 files changed, 689 insertions(+), 185 deletions(-)

diff --git 
a/build-utils/language-go/src/main/resources/templates/go/model-template.go.ftlh
 
b/build-utils/language-go/src/main/resources/templates/go/model-template.go.ftlh
index 5af59d7..5d138f5 100644
--- 
a/build-utils/language-go/src/main/resources/templates/go/model-template.go.ftlh
+++ 
b/build-utils/language-go/src/main/resources/templates/go/model-template.go.ftlh
@@ -544,7 +544,7 @@ func ${type.name}Parse(io utils.ReadBuffer<#if 
type.parserArguments?has_content>
                                <#assign arrayField = field>
 
        // Array field (${arrayField.name})
-       io.PullContext("${arrayField.name}")
+       io.PullContext("${arrayField.name}", utils.WithRenderAsList(true))
                        <#-- Only update curPos if the length expression uses 
it -->
                                <#if 
arrayField.loopExpression.contains("curPos")>
        curPos = io.GetPos() - startPos<@emitImport import="io" />
@@ -598,7 +598,7 @@ func ${type.name}Parse(io utils.ReadBuffer<#if 
type.parserArguments?has_content>
                                        </#if>
        }
                                </#if>
-       io.CloseContext("${arrayField.name}")
+       io.CloseContext("${arrayField.name}", utils.WithRenderAsList(true))
                                <#break>
                        <#case "checksum">
                                <#assign checksumField = field>
@@ -691,7 +691,7 @@ func ${type.name}Parse(io utils.ReadBuffer<#if 
type.parserArguments?has_content>
                                <#break>
                        <#case "manualArray">
                                <#assign manualArrayField = field>
-       io.PullContext("${manualArrayField.name}")
+       io.PullContext("${manualArrayField.name}", utils.WithRenderAsList(true))
        // Manual Array Field (${manualArrayField.name})
                        <#-- Only update curPos if the length expression uses 
it -->
                                <#if 
manualArrayField.loopExpression.contains("curPos")>
@@ -747,7 +747,7 @@ func ${type.name}Parse(io utils.ReadBuffer<#if 
type.parserArguments?has_content>
        ${helper.getLanguageTypeNameForField(field)}[] ${manualArrayField.name} 
= _${manualArrayField.name}List.toArray(new 
${helper.getLanguageTypeNameForField(manualArrayField)}[0])
                                        </#if>
                                </#if>
-       io.CloseContext("${manualArrayField.name}")
+       io.CloseContext("${manualArrayField.name}", 
utils.WithRenderAsList(true))
                                <#break>
                        <#case "manual">
                                <#assign manualField = field>
@@ -1043,11 +1043,11 @@ func (m *${type.name}) Serialize(io 
utils.WriteBuffer<#if helper.getSerializerAr
 
        // Manual Array Field (${manualArrayField.name})
        if m.${manualArrayField.name?cap_first} != nil {
-               io.PushContext("${manualArrayField.name}")
+               io.PushContext("${manualArrayField.name}", 
utils.WithRenderAsList(true))
                for(${helper.getLanguageTypeNameForField(field)} element : 
m.${manualArrayField.name?cap_first}) {
                        ${helper.toSerializationExpression(manualArrayField, 
manualArrayField.serializeExpression, type.parserArguments)}
                }
-               io.PushContext("${manualArrayField.name}")
+               io.PopContext("${manualArrayField.name}", 
utils.WithRenderAsList(true))
        }
                                <#break>
                        <#case "manual">
diff --git a/go.mod b/go.mod
index eebd510..751a141 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,6 @@ module github.com/apache/plc4x
 go 1.15
 
 require (
-       github.com/apache/plc4x/plc4go v0.0.0-20210420140858-e7f2f4c6b117 // 
indirect
+       github.com/apache/plc4x/plc4go v0.0.0-20210420175605-7ec5b56fe1cf // 
indirect
        github.com/sirupsen/logrus v1.7.0 // indirect
 )
diff --git a/go.sum b/go.sum
index e36d6ad..c53de2f 100644
--- a/go.sum
+++ b/go.sum
@@ -96,6 +96,8 @@ github.com/apache/plc4x/plc4go 
v0.0.0-20210419213911-a28fac5a883e h1:RH0AQ+AnzYJ
 github.com/apache/plc4x/plc4go v0.0.0-20210419213911-a28fac5a883e/go.mod 
h1:NqpWaMMhsu3sTm418XwsUWgmY7e2zjgpRMyddBUwmGk=
 github.com/apache/plc4x/plc4go v0.0.0-20210420140858-e7f2f4c6b117 
h1:aJkJp07IONjywarB9o0DlWyFRwPJIgn+Nfn1jxJz86s=
 github.com/apache/plc4x/plc4go v0.0.0-20210420140858-e7f2f4c6b117/go.mod 
h1:NqpWaMMhsu3sTm418XwsUWgmY7e2zjgpRMyddBUwmGk=
+github.com/apache/plc4x/plc4go v0.0.0-20210420175605-7ec5b56fe1cf 
h1:HYsSjhzqXX2CWIP9iF+or7q3bLJEvB3XBx146pejQ04=
+github.com/apache/plc4x/plc4go v0.0.0-20210420175605-7ec5b56fe1cf/go.mod 
h1:NqpWaMMhsu3sTm418XwsUWgmY7e2zjgpRMyddBUwmGk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dnephin/pflag v1.0.7/go.mod 
h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
diff --git a/plc4go/internal/plc4go/s7/readwrite/model/COTPPacket.go 
b/plc4go/internal/plc4go/s7/readwrite/model/COTPPacket.go
index a5b5c8b..fca65b6 100644
--- a/plc4go/internal/plc4go/s7/readwrite/model/COTPPacket.go
+++ b/plc4go/internal/plc4go/s7/readwrite/model/COTPPacket.go
@@ -159,7 +159,7 @@ func COTPPacketParse(io utils.ReadBuffer, cotpLen uint16) 
(*COTPPacket, error) {
        }
 
        // Array field (parameters)
-       io.PullContext("parameters")
+       io.PullContext("parameters", utils.WithRenderAsList(true))
        curPos = io.GetPos() - startPos
        // Length array
        parameters := make([]*COTPParameter, 0)
@@ -173,7 +173,7 @@ func COTPPacketParse(io utils.ReadBuffer, cotpLen uint16) 
(*COTPPacket, error) {
                parameters = append(parameters, _item)
                curPos = io.GetPos() - startPos
        }
-       io.CloseContext("parameters")
+       io.CloseContext("parameters", utils.WithRenderAsList(true))
 
        // Optional Field (payload) (Can be skipped, if a given expression 
evaluates to false)
        curPos = io.GetPos() - startPos
@@ -223,14 +223,14 @@ func (m *COTPPacket) SerializeParent(io 
utils.WriteBuffer, child ICOTPPacket, se
 
        // Array Field (parameters)
        if m.Parameters != nil {
-               io.PushContext("parameters")
+               io.PushContext("parameters", utils.WithRenderAsList(true))
                for _, _element := range m.Parameters {
                        _elementErr := _element.Serialize(io)
                        if _elementErr != nil {
                                return errors.Wrap(_elementErr, "Error 
serializing 'parameters' field")
                        }
                }
-               io.PopContext("parameters")
+               io.PopContext("parameters", utils.WithRenderAsList(true))
        }
 
        // Optional Field (payload) (Can be skipped, if the value is null)
diff --git a/plc4go/internal/plc4go/s7/s7Io_test.go 
b/plc4go/internal/plc4go/s7/s7Io_test.go
index 33cec3c..e803ab3 100644
--- a/plc4go/internal/plc4go/s7/s7Io_test.go
+++ b/plc4go/internal/plc4go/s7/s7Io_test.go
@@ -43,6 +43,7 @@ func TestS7MessageBytes(t *testing.T) {
                wantString           string
                wantStringSerialized string
                wantStringXml        string
+               wantStringJson       string
                wantDump             string
        }{
                {
@@ -220,6 +221,126 @@ func TestS7MessageBytes(t *testing.T) {
   </payload>
 </TPKTPacket>
 `,
+                       wantStringJson: `
+{
+  "TPKTPacket": {
+    "len": 29,
+    "len__plc4x_bitLength": 16,
+    "len__plc4x_dataType": "uint",
+    "payload": {
+      "COTPPacket": {
+        "COTPPacketData": {
+          "eot": false,
+          "eot__plc4x_bitLength": 1,
+          "eot__plc4x_dataType": "bit",
+          "tpduRef": 13,
+          "tpduRef__plc4x_bitLength": 7,
+          "tpduRef__plc4x_dataType": "uint"
+        },
+        "S7Message": {
+          "S7MessageResponseData": {
+            "errorClass": 0,
+            "errorClass__plc4x_bitLength": 8,
+            "errorClass__plc4x_dataType": "uint",
+            "errorCode": 0,
+            "errorCode__plc4x_bitLength": 8,
+            "errorCode__plc4x_dataType": "uint"
+          },
+          "S7Parameter": {
+            "S7ParameterReadVarResponse": {
+              "numItems": 1,
+              "numItems__plc4x_bitLength": 8,
+              "numItems__plc4x_dataType": "uint"
+            },
+            "parameterType": 4,
+            "parameterType__plc4x_bitLength": 8,
+            "parameterType__plc4x_dataType": "uint"
+          },
+          "S7Payload": {
+            "S7PayloadReadVarResponse": {
+              "items": {
+                "S7VarPayloadDataItem": {
+                  "data": {
+                    "value": 1,
+                    "value__plc4x_bitLength": 8,
+                    "value__plc4x_dataType": "int"
+                  },
+                  "dataLength": 1,
+                  "dataLength__plc4x_bitLength": 16,
+                  "dataLength__plc4x_dataType": "uint",
+                  "padding": {},
+                  "returnCode": {
+                    "DataTransportErrorCode": 255,
+                    "DataTransportErrorCode__plc4x_bitLength": 8,
+                    "DataTransportErrorCode__plc4x_dataType": "uint",
+                    "DataTransportErrorCode__plc4x_stringRepresentation": "OK"
+                  },
+                  "transportSize": {
+                    "DataTransportSize": 3,
+                    "DataTransportSize__plc4x_bitLength": 8,
+                    "DataTransportSize__plc4x_dataType": "uint",
+                    "DataTransportSize__plc4x_stringRepresentation": "BIT"
+                  }
+                }
+              }
+            }
+          },
+          "messageType": 3,
+          "messageType__plc4x_bitLength": 8,
+          "messageType__plc4x_dataType": "uint",
+          "parameterLength": 2,
+          "parameterLength__plc4x_bitLength": 16,
+          "parameterLength__plc4x_dataType": "uint",
+          "payloadLength": 5,
+          "payloadLength__plc4x_bitLength": 16,
+          "payloadLength__plc4x_dataType": "uint",
+          "protocolId": 50,
+          "protocolId__plc4x_bitLength": 8,
+          "protocolId__plc4x_dataType": "uint",
+          "reserved": 0,
+          "reserved__plc4x_bitLength": 16,
+          "reserved__plc4x_dataType": "uint",
+          "tpduReference": 11,
+          "tpduReference__plc4x_bitLength": 16,
+          "tpduReference__plc4x_dataType": "uint"
+        },
+        "headerLength": 5,
+        "headerLength__plc4x_bitLength": 8,
+        "headerLength__plc4x_dataType": "uint",
+        "parameters": [
+          {
+            "COTPParameter": {
+              "COTPParameterTpduSize": {
+                "tpduSize": {
+                  "COTPTpduSize": 12,
+                  "COTPTpduSize__plc4x_bitLength": 8,
+                  "COTPTpduSize__plc4x_dataType": "int",
+                  "COTPTpduSize__plc4x_stringRepresentation": "SIZE_4096"
+                }
+              },
+              "parameterLength": 1,
+              "parameterLength__plc4x_bitLength": 8,
+              "parameterLength__plc4x_dataType": "uint",
+              "parameterType": 192,
+              "parameterType__plc4x_bitLength": 8,
+              "parameterType__plc4x_dataType": "uint"
+            }
+          }
+        ],
+        "tpduCode": 240,
+        "tpduCode__plc4x_bitLength": 8,
+        "tpduCode__plc4x_dataType": "uint"
+      }
+    },
+    "protocolId": 3,
+    "protocolId__plc4x_bitLength": 8,
+    "protocolId__plc4x_dataType": "uint",
+    "reserved": 0,
+    "reserved__plc4x_bitLength": 8,
+    "reserved__plc4x_dataType": "uint"
+  }
+}
+`,
                        wantDump: `
 00|03 00 00 1d 05 f0 0d c0 01 0c '..........'
 10|32 03 00 00 00 0b 00 02 00 05 '2.........'
@@ -229,47 +350,71 @@ func TestS7MessageBytes(t *testing.T) {
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       // Simple 2 String
-                       tt.wantString = strings.Trim(tt.wantString, "\n")
-                       if got := tt.args.debuggable.String(); got != 
tt.wantString {
-                               t.Errorf("String() = '\n%v\n', want '\n%v\n'", 
got, tt.wantString)
-                       }
-                       // Simple 2 Box
-                       boxWriter := utils.NewBoxedWriteBuffer()
-                       if err := tt.args.debuggable.Serialize(boxWriter); err 
!= nil {
-                               t.Error(err)
-                       }
-                       tt.wantStringSerialized = 
strings.Trim(tt.wantStringSerialized, "\n")
-                       if got := string(boxWriter.GetBox()); got != 
tt.wantStringSerialized {
-                               t.Errorf("Serialize Boxed() = '\n%v\n', want 
'\n%v\n'", got, tt.wantStringSerialized)
-                       }
-                       // Simple 2 Xml
-                       xmlWriteBuffer := utils.NewXmlWriteBuffer()
-                       if err := tt.args.debuggable.Serialize(xmlWriteBuffer); 
err != nil {
-                               t.Error(err)
+                       {
+                               // Simple 2 String
+                               tt.wantString = strings.Trim(tt.wantString, 
"\n")
+                               if got := tt.args.debuggable.String(); got != 
tt.wantString {
+                                       t.Errorf("String() = '\n%v\n', want 
'\n%v\n'", got, tt.wantString)
+                               }
                        }
-                       tt.wantStringXml = strings.Trim(tt.wantStringXml, "\n")
-                       if got := xmlWriteBuffer.GetXmlString(); got != 
tt.wantStringXml {
-                               t.Errorf("Serialize Xml() = '\n%v\n', want 
'\n%v\n'", got, tt.wantStringXml)
+                       {
+                               // Simple 2 Box
+                               boxWriter := utils.NewBoxedWriteBuffer()
+                               if err := 
tt.args.debuggable.Serialize(boxWriter); err != nil {
+                                       t.Error(err)
+                               }
+                               tt.wantStringSerialized = 
strings.Trim(tt.wantStringSerialized, "\n")
+                               if got := string(boxWriter.GetBox()); got != 
tt.wantStringSerialized {
+                                       t.Errorf("Serialize Boxed() = '\n%v\n', 
want '\n%v\n'", got, tt.wantStringSerialized)
+                               }
                        }
-
-                       // Simple Binary Serialize
-                       buffer := utils.NewWriteBuffer()
-                       if err := tt.args.debuggable.Serialize(buffer); err != 
nil {
-                               t.Error(err)
+                       {
+                               // Simple 2 Xml
+                               xmlWriteBuffer := utils.NewXmlWriteBuffer()
+                               if err := 
tt.args.debuggable.Serialize(xmlWriteBuffer); err != nil {
+                                       t.Error(err)
+                               }
+                               tt.wantStringXml = 
strings.Trim(tt.wantStringXml, "\n")
+                               if got := xmlWriteBuffer.GetXmlString(); got != 
tt.wantStringXml {
+                                       t.Errorf("Serialize Xml() = '\n%v\n', 
want '\n%v\n'", got, tt.wantStringXml)
+                               }
                        }
-                       tt.wantDump = strings.Trim(tt.wantDump, "\n")
-                       if got := utils.Dump(buffer.GetBytes()); 
!reflect.DeepEqual(got, tt.wantDump) {
-                               t.Errorf("Serialize() = '\n%v\n', want 
'\n%v\n'", got, tt.wantDump)
+                       {
+                               // Simple 2 Json
+                               jsonWriteBuffer := utils.NewJsonWriteBuffer()
+                               if err := 
tt.args.debuggable.Serialize(jsonWriteBuffer); err != nil {
+                                       t.Error(err)
+                               }
+                               tt.wantStringJson = 
strings.Trim(tt.wantStringJson, "\n")
+                               if got, err := jsonWriteBuffer.GetJsonString(); 
err != nil || strings.Trim(got, "\n") != tt.wantStringJson {
+                                       if err != nil {
+                                               t.Error(err)
+                                       } else {
+                                               t.Errorf("Serialize Json() = 
'\n%v\n', want '\n%v\n'", got, tt.wantStringJson)
+                                       }
+                               }
                        }
-                       // and at least a roundtip
-                       reader := strings.NewReader(tt.wantStringXml)
-                       readBuffer := utils.NewXmlReadBuffer(reader)
-                       if got, err := model.TPKTPacketParse(readBuffer); err 
!= nil || !reflect.DeepEqual(got, tt.args.debuggable) {
-                               if err != nil {
+                       {
+                               // Simple Binary Serialize
+                               buffer := utils.NewWriteBuffer()
+                               if err := tt.args.debuggable.Serialize(buffer); 
err != nil {
                                        t.Error(err)
-                               } else {
-                                       t.Errorf("Roundtrip = '\n%v\n', want 
'\n%v\n'", got, tt.wantDump)
+                               }
+                               tt.wantDump = strings.Trim(tt.wantDump, "\n")
+                               if got := utils.Dump(buffer.GetBytes()); 
!reflect.DeepEqual(got, tt.wantDump) {
+                                       t.Errorf("Serialize() = '\n%v\n', want 
'\n%v\n'", got, tt.wantDump)
+                               }
+                       }
+                       {
+                               // and at least a roundtip
+                               reader := strings.NewReader(tt.wantStringXml)
+                               readBuffer := utils.NewXmlReadBuffer(reader)
+                               if got, err := 
model.TPKTPacketParse(readBuffer); err != nil || !reflect.DeepEqual(got, 
tt.args.debuggable) {
+                                       if err != nil {
+                                               t.Error(err)
+                                       } else {
+                                               t.Errorf("Roundtrip = '\n%v\n', 
want '\n%v\n'", got, tt.wantDump)
+                                       }
                                }
                        }
                })
diff --git a/plc4go/internal/plc4go/spi/utils/Buffer.go 
b/plc4go/internal/plc4go/spi/utils/Buffer.go
new file mode 100644
index 0000000..99df612
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/utils/Buffer.go
@@ -0,0 +1,85 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package utils
+
+// WithReaderWriterArgs is a marker interface for reader args supplied by the 
builders
+type WithReaderWriterArgs interface {
+       WithReaderArgs
+       WithWriterArgs
+}
+
+// WithAdditionalStringRepresentation can be used by e.g. enums to supply an 
additional string representation
+func WithAdditionalStringRepresentation(stringRepresentation string) 
WithReaderWriterArgs {
+       return withAdditionalStringRepresentation{stringRepresentation: 
stringRepresentation}
+}
+
+// WithRenderAsList indicates that an element can be rendered as list
+func WithRenderAsList(renderAsList bool) WithReaderWriterArgs {
+       return withRenderAsList{renderAsList: renderAsList}
+}
+
+///////////////////////////////////////
+///////////////////////////////////////
+//
+// Internal section
+//
+
+type readerWriterArg struct {
+}
+
+func (_ readerWriterArg) isWriterArgs() bool {
+       return true
+}
+
+func (_ readerWriterArg) isReaderArgs() bool {
+       return true
+}
+
+type withAdditionalStringRepresentation struct {
+       readerWriterArg
+       stringRepresentation string
+}
+
+type withRenderAsList struct {
+       readerWriterArg
+       renderAsList bool
+}
+
+func upcastReaderArgs(args ...WithReaderArgs) []WithReaderWriterArgs {
+       result := make([]WithReaderWriterArgs, len(args))
+       for i, arg := range args {
+               result[i] = arg.(WithReaderWriterArgs)
+       }
+       return result
+}
+
+func upcastWriterArgs(args ...WithWriterArgs) []WithReaderWriterArgs {
+       result := make([]WithReaderWriterArgs, len(args))
+       for i, arg := range args {
+               result[i] = arg.(WithReaderWriterArgs)
+       }
+       return result
+}
+
+//
+// Internal section
+//
+///////////////////////////////////////
+///////////////////////////////////////
diff --git a/plc4go/internal/plc4go/spi/utils/ReadBufferXmlBased.go 
b/plc4go/internal/plc4go/spi/utils/ReadBufferXmlBased.go
index cb8434f..21e1729 100644
--- a/plc4go/internal/plc4go/spi/utils/ReadBufferXmlBased.go
+++ b/plc4go/internal/plc4go/spi/utils/ReadBufferXmlBased.go
@@ -29,8 +29,8 @@ import (
 
 func NewXmlReadBuffer(reader io.Reader) ReadBuffer {
        return &xmlReadBuffer{
-               xml.NewDecoder(reader),
-               1,
+               Decoder: xml.NewDecoder(reader),
+               pos:     1,
        }
 }
 
@@ -41,6 +41,7 @@ func NewXmlReadBuffer(reader io.Reader) ReadBuffer {
 //
 
 type xmlReadBuffer struct {
+       bufferCommons
        *xml.Decoder
        pos uint
 }
@@ -73,7 +74,7 @@ func (x *xmlReadBuffer) PullContext(logicalName string, 
readerArgs ...WithReader
 
 func (x *xmlReadBuffer) ReadBit(logicalName string, readerArgs 
...WithReaderArgs) (bool, error) {
        var value bool
-       err := x.decode(logicalName, "bit", 1, readerArgs, &value)
+       err := x.decode(logicalName, rwBitKey, 1, readerArgs, &value)
        if err != nil {
                return false, err
        }
@@ -83,7 +84,7 @@ func (x *xmlReadBuffer) ReadBit(logicalName string, 
readerArgs ...WithReaderArgs
 
 func (x *xmlReadBuffer) ReadUint8(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint8, error) {
        var value uint8
-       err := x.decode(logicalName, "uint", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwUintKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -93,7 +94,7 @@ func (x *xmlReadBuffer) ReadUint8(logicalName string, 
bitLength uint8, readerArg
 
 func (x *xmlReadBuffer) ReadUint16(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint16, error) {
        var value uint16
-       err := x.decode(logicalName, "uint", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwUintKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -103,7 +104,7 @@ func (x *xmlReadBuffer) ReadUint16(logicalName string, 
bitLength uint8, readerAr
 
 func (x *xmlReadBuffer) ReadUint32(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint32, error) {
        var value uint32
-       err := x.decode(logicalName, "uint", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwUintKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -113,7 +114,7 @@ func (x *xmlReadBuffer) ReadUint32(logicalName string, 
bitLength uint8, readerAr
 
 func (x *xmlReadBuffer) ReadUint64(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (uint64, error) {
        var value uint64
-       err := x.decode(logicalName, "uint", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwUintKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -123,7 +124,7 @@ func (x *xmlReadBuffer) ReadUint64(logicalName string, 
bitLength uint8, readerAr
 
 func (x *xmlReadBuffer) ReadInt8(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int8, error) {
        var value int8
-       err := x.decode(logicalName, "int", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwIntKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -133,7 +134,7 @@ func (x *xmlReadBuffer) ReadInt8(logicalName string, 
bitLength uint8, readerArgs
 
 func (x *xmlReadBuffer) ReadInt16(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int16, error) {
        var value int16
-       err := x.decode(logicalName, "int", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwIntKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -143,7 +144,7 @@ func (x *xmlReadBuffer) ReadInt16(logicalName string, 
bitLength uint8, readerArg
 
 func (x *xmlReadBuffer) ReadInt32(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int32, error) {
        var value int32
-       err := x.decode(logicalName, "int", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwIntKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -153,7 +154,7 @@ func (x *xmlReadBuffer) ReadInt32(logicalName string, 
bitLength uint8, readerArg
 
 func (x *xmlReadBuffer) ReadInt64(logicalName string, bitLength uint8, 
readerArgs ...WithReaderArgs) (int64, error) {
        var value int64
-       err := x.decode(logicalName, "int", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwIntKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -164,7 +165,7 @@ func (x *xmlReadBuffer) ReadInt64(logicalName string, 
bitLength uint8, readerArg
 func (x *xmlReadBuffer) ReadBigInt(logicalName string, bitLength uint64, 
readerArgs ...WithReaderArgs) (*big.Int, error) {
        var value big.Int
        // TODO: bitLength is too short for a big int
-       err := x.decode(logicalName, "int", uint8(bitLength), readerArgs, 
&value)
+       err := x.decode(logicalName, rwIntKey, uint8(bitLength), readerArgs, 
&value)
        if err != nil {
                return nil, err
        }
@@ -178,7 +179,7 @@ func (x *xmlReadBuffer) ReadFloat32(logicalName string, 
signed bool, exponentBit
                bitLength += 1
        }
        var value float32
-       err := x.decode(logicalName, "float", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwFloatKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -192,7 +193,7 @@ func (x *xmlReadBuffer) ReadFloat64(logicalName string, 
signed bool, exponentBit
                bitLength += 1
        }
        var value float64
-       err := x.decode(logicalName, "float", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwFloatKey, bitLength, readerArgs, &value)
        if err != nil {
                return 0, err
        }
@@ -206,7 +207,7 @@ func (x *xmlReadBuffer) ReadBigFloat(logicalName string, 
signed bool, exponentBi
                bitLength += 1
        }
        var value big.Float
-       err := x.decode(logicalName, "float", bitLength, readerArgs, &value)
+       err := x.decode(logicalName, rwFloatKey, bitLength, readerArgs, &value)
        if err != nil {
                return nil, err
        }
@@ -217,7 +218,7 @@ func (x *xmlReadBuffer) ReadBigFloat(logicalName string, 
signed bool, exponentBi
 func (x *xmlReadBuffer) ReadString(logicalName string, bitLength uint32, 
readerArgs ...WithReaderArgs) (string, error) {
        var value string
        // TODO: bitlength too short
-       err := x.decode(logicalName, "string", uint8(bitLength), readerArgs, 
&value)
+       err := x.decode(logicalName, rwStringKey, uint8(bitLength), readerArgs, 
&value)
        if err != nil {
                return "", err
        }
@@ -279,7 +280,7 @@ func (x *xmlReadBuffer) decode(logicalName string, dataType 
string, bitLength ui
        if err != nil {
                return err
        }
-       err = validateStartElement(startElement, logicalName, dataType, 
bitLength, readerArgs...)
+       err = x.validateStartElement(startElement, logicalName, dataType, 
bitLength, readerArgs...)
        if err != nil {
                return err
        }
@@ -290,38 +291,38 @@ func (x *xmlReadBuffer) decode(logicalName string, 
dataType string, bitLength ui
        return nil
 }
 
-func validateStartElement(startElement xml.StartElement, logicalName string, 
dataType string, bitLength uint8, readerArgs ...WithReaderArgs) error {
-       logicalName = sanitizeLogicalName(logicalName)
+func (x *xmlReadBuffer) validateStartElement(startElement xml.StartElement, 
logicalName string, dataType string, bitLength uint8, readerArgs 
...WithReaderArgs) error {
+       logicalName = x.sanitizeLogicalName(logicalName)
        if startElement.Name.Local != logicalName {
                return errors.Errorf("unexpected element '%s'. Expected '%s'", 
startElement.Name.Local, logicalName)
-       } else if err := validateAttr(startElement.Attr, dataType, bitLength, 
readerArgs...); err != nil {
+       } else if err := x.validateAttr(startElement.Attr, dataType, bitLength, 
readerArgs...); err != nil {
                return errors.Wrap(err, "Error validating Attributes")
        }
        return nil
 }
 
-func validateAttr(attr []xml.Attr, dataType string, bitLength uint8, 
readerArgs ...WithReaderArgs) error {
+func (x *xmlReadBuffer) validateAttr(attr []xml.Attr, dataType string, 
bitLength uint8, readerArgs ...WithReaderArgs) error {
        dataTypeValidated := false
        bitLengthValidate := false
        for _, attribute := range attr {
                switch attribute.Name.Local {
-               case "dataType":
+               case rwDataTypeKey:
                        if attribute.Value != dataType {
-                               return errors.Errorf("Unexpected dataType :%s. 
Want %s", attribute.Value, dataType)
+                               return errors.Errorf("Unexpected %s :%s. Want 
%s", rwDataTypeKey, attribute.Value, dataType)
                        }
                        dataTypeValidated = true
-               case "bitLength":
+               case rwBitLengthKey:
                        if attribute.Value != fmt.Sprintf("%d", bitLength) {
-                               return errors.Errorf("Unexpected bitLength 
'%s'. Want '%d'", attribute.Value, bitLength)
+                               return errors.Errorf("Unexpected %s '%s'. Want 
'%d'", rwBitLengthKey, attribute.Value, bitLength)
                        }
                        bitLengthValidate = true
                }
        }
        if !dataTypeValidated {
-               return errors.New("required attribute dataType missing")
+               return errors.Errorf("required attribute %s missing", 
rwDataTypeKey)
        }
        if !bitLengthValidate {
-               return errors.New("required attribute bitLength missing")
+               return errors.Errorf("required attribute %s missing", 
rwBitLengthKey)
        }
        return nil
 }
diff --git a/plc4go/internal/plc4go/spi/utils/WriteBufferBoxBased.go 
b/plc4go/internal/plc4go/spi/utils/WriteBufferBoxBased.go
index 34acf88..24b5e68 100644
--- a/plc4go/internal/plc4go/spi/utils/WriteBufferBoxBased.go
+++ b/plc4go/internal/plc4go/spi/utils/WriteBufferBoxBased.go
@@ -38,23 +38,14 @@ func NewBoxedWriteBuffer() WriteBufferBoxBased {
        }
 }
 
-// WithAdditionalStringRepresentation can be used by e.g. enums to supply an 
additional string representation
-func WithAdditionalStringRepresentation(stringRepresentation string) 
WithWriterArgs {
-       return withAdditionalStringRepresentation{stringRepresentation: 
stringRepresentation}
-}
-
 ///////////////////////////////////////
 ///////////////////////////////////////
 //
 // Internal section
 //
 
-type withAdditionalStringRepresentation struct {
-       writerArg
-       stringRepresentation string
-}
-
 type boxedWriteBuffer struct {
+       bufferCommons
        *list.List
        desiredWidth int
        currentWidth int
@@ -78,7 +69,7 @@ func (b *boxedWriteBuffer) PushContext(_ string, _ 
...WithWriterArgs) error {
 }
 
 func (b *boxedWriteBuffer) WriteBit(logicalName string, value bool, writerArgs 
...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        asInt := 0
        if value {
                asInt = 1
@@ -88,79 +79,79 @@ func (b *boxedWriteBuffer) WriteBit(logicalName string, 
value bool, writerArgs .
 }
 
 func (b *boxedWriteBuffer) WriteUint8(logicalName string, bitLength uint8, 
value uint8, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteUint16(logicalName string, bitLength uint8, 
value uint16, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteUint32(logicalName string, bitLength uint8, 
value uint32, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteUint64(logicalName string, bitLength uint8, 
value uint64, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteInt8(logicalName string, bitLength uint8, 
value int8, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteInt16(logicalName string, bitLength uint8, 
value int16, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteInt32(logicalName string, bitLength uint8, 
value int32, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteInt64(logicalName string, bitLength uint8, 
value int64, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteBigInt(logicalName string, bitLength uint8, 
value *big.Int, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %d%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteFloat32(logicalName string, bitLength uint8, 
value float32, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %f%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteFloat64(logicalName string, bitLength uint8, 
value float64, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %f%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteBigFloat(logicalName string, bitLength uint8, 
value *big.Float, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %f%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
 
 func (b *boxedWriteBuffer) WriteString(logicalName string, bitLength uint8, _ 
string, value string, writerArgs ...WithWriterArgs) error {
-       additionalStringRepresentation := 
extractAdditionalStringRepresentation(writerArgs...)
+       additionalStringRepresentation := 
b.extractAdditionalStringRepresentation(upcastWriterArgs(writerArgs...)...)
        b.PushBack(BoxString(logicalName, fmt.Sprintf("%#0*x %s%s", 
bitLength/4, value, value, additionalStringRepresentation), 0))
        return nil
 }
@@ -187,16 +178,3 @@ findTheBox:
        b.PushBack(asciiBox)
        return nil
 }
-
-func extractAdditionalStringRepresentation(writerArgs ...WithWriterArgs) 
string {
-       for _, arg := range writerArgs {
-               if !arg.isWriterArgs() {
-                       panic("not a writer arg")
-               }
-               switch arg.(type) {
-               case withAdditionalStringRepresentation:
-                       return " " + 
arg.(withAdditionalStringRepresentation).stringRepresentation
-               }
-       }
-       return ""
-}
diff --git a/plc4go/internal/plc4go/spi/utils/WriteBufferJsonBased.go 
b/plc4go/internal/plc4go/spi/utils/WriteBufferJsonBased.go
new file mode 100644
index 0000000..237b12c
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/utils/WriteBufferJsonBased.go
@@ -0,0 +1,232 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package utils
+
+import (
+       "encoding/json"
+       "fmt"
+       "github.com/pkg/errors"
+       "math/big"
+       "strings"
+)
+
+type WriteBufferJsonBased interface {
+       WriteBuffer
+       GetJsonString() (string, error)
+}
+
+func NewJsonWriteBuffer() WriteBufferJsonBased {
+       var jsonString strings.Builder
+       encoder := json.NewEncoder(&jsonString)
+       encoder.SetIndent("", "  ")
+       return &jsonWriteBuffer{
+               jsonString: &jsonString,
+               Encoder:    encoder,
+       }
+}
+
+///////////////////////////////////////
+///////////////////////////////////////
+//
+// Internal section
+//
+
+type jsonWriteBuffer struct {
+       bufferCommons
+       stack
+       *json.Encoder
+       jsonString *strings.Builder
+       rootNode   interface{}
+}
+
+type elementContext struct {
+       logicalName string
+       properties  map[string]interface{}
+}
+
+type listContext struct {
+       logicalName string
+       list        []interface{}
+}
+
+//
+// Internal section
+//
+///////////////////////////////////////
+///////////////////////////////////////
+
+func (j *jsonWriteBuffer) PushContext(logicalName string, writerArgs 
...WithWriterArgs) error {
+       renderedAsList := 
j.isToBeRenderedAsList(upcastWriterArgs(writerArgs...)...)
+       if renderedAsList {
+               j.Push(&listContext{logicalName, make([]interface{}, 0)})
+       } else {
+               j.Push(&elementContext{logicalName, 
make(map[string]interface{})})
+       }
+       return nil
+}
+
+func (j *jsonWriteBuffer) WriteBit(logicalName string, value bool, writerArgs 
...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwBitKey, 1, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteUint8(logicalName string, bitLength uint8, 
value uint8, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwUintKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteUint16(logicalName string, bitLength uint8, 
value uint16, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwUintKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteUint32(logicalName string, bitLength uint8, 
value uint32, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwUintKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteUint64(logicalName string, bitLength uint8, 
value uint64, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwUintKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteInt8(logicalName string, bitLength uint8, value 
int8, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwIntKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteInt16(logicalName string, bitLength uint8, 
value int16, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwIntKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteInt32(logicalName string, bitLength uint8, 
value int32, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwIntKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteInt64(logicalName string, bitLength uint8, 
value int64, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwIntKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteBigInt(logicalName string, bitLength uint8, 
value *big.Int, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwIntKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteFloat32(logicalName string, bitLength uint8, 
value float32, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwFloatKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteFloat64(logicalName string, bitLength uint8, 
value float64, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwFloatKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteBigFloat(logicalName string, bitLength uint8, 
value *big.Float, writerArgs ...WithWriterArgs) error {
+       return j.encodeNode(logicalName, value, j.generateAttr(logicalName, 
rwFloatKey, bitLength, writerArgs...))
+}
+
+func (j *jsonWriteBuffer) WriteString(logicalName string, bitLength uint8, 
encoding string, value string, writerArgs ...WithWriterArgs) error {
+       attr := j.generateAttr(logicalName, rwStringKey, bitLength, 
writerArgs...)
+       attr[fmt.Sprintf("__plc4x_%s", rwEncodingKey)] = encoding
+       return j.encodeNode(logicalName, value, attr)
+}
+
+func (j *jsonWriteBuffer) PopContext(logicalName string, _ ...WithWriterArgs) 
error {
+       pop := j.Pop()
+       var poppedName string
+       var unwrapped interface{}
+       switch pop.(type) {
+       case *elementContext:
+               context := pop.(*elementContext)
+               poppedName = context.logicalName
+               unwrapped = context.properties
+       case *listContext:
+               context := pop.(*listContext)
+               poppedName = context.logicalName
+               unwrapped = context.list
+       default:
+               panic("broken context")
+       }
+       if poppedName != logicalName {
+               return errors.Errorf("unexpected closing context %s, expected 
%s", poppedName, logicalName)
+       }
+       if j.Empty() {
+               lastElement := make(map[string]interface{})
+               lastElement[logicalName] = unwrapped
+               j.rootNode = lastElement
+               return nil
+       }
+       j.rootNode = j.Peek()
+       switch j.rootNode.(type) {
+       case *elementContext:
+               context := j.rootNode.(*elementContext)
+               context.properties[logicalName] = unwrapped
+       case *listContext:
+               context := j.rootNode.(*listContext)
+               wrappedWrap := make(map[string]interface{})
+               wrappedWrap[logicalName] = unwrapped
+               context.list = append(context.list, wrappedWrap)
+       default:
+               panic("broken context")
+       }
+       return nil
+}
+
+func (j *jsonWriteBuffer) GetJsonString() (string, error) {
+       if j.rootNode == nil {
+               return "", errors.New("No content available")
+       }
+       err := j.Encode(j.rootNode)
+       if err != nil {
+               return "", err
+       }
+       return j.jsonString.String(), nil
+}
+
+func (j *jsonWriteBuffer) encodeNode(logicalName string, value interface{}, 
attr map[string]interface{}, _ ...WithWriterArgs) error {
+       logicalName = j.sanitizeLogicalName(logicalName)
+       peek := j.Peek()
+       switch peek.(type) {
+       case *elementContext:
+               context := peek.(*elementContext)
+               context.properties[logicalName] = value
+               for key, attrValue := range attr {
+                       context.properties[key] = attrValue
+               }
+               return nil
+       case *listContext:
+               context := peek.(*listContext)
+               m := make(map[string]interface{})
+               m[logicalName] = value
+               context.list = append(context.list, m)
+               return nil
+       default:
+               panic("broken context")
+       }
+}
+
+func (j *jsonWriteBuffer) generateAttr(logicalName string, dataType string, 
bitLength uint8, writerArgs ...WithWriterArgs) map[string]interface{} {
+       logicalName = j.sanitizeLogicalName(logicalName)
+       attr := make(map[string]interface{})
+       attr[fmt.Sprintf("%s__plc4x_%s", logicalName, rwDataTypeKey)] = dataType
+       attr[fmt.Sprintf("%s__plc4x_%s", logicalName, rwBitLengthKey)] = 
bitLength
+       for _, arg := range writerArgs {
+               if !arg.isWriterArgs() {
+                       panic("not a writer arg")
+               }
+               switch arg.(type) {
+               case withAdditionalStringRepresentation:
+                       attr[fmt.Sprintf("%s__plc4x_%s", logicalName, 
rwStringRepresentationKey)] = 
arg.(withAdditionalStringRepresentation).stringRepresentation
+               }
+       }
+       return attr
+}
diff --git a/plc4go/internal/plc4go/spi/utils/WriteBufferXmlBased.go 
b/plc4go/internal/plc4go/spi/utils/WriteBufferXmlBased.go
index 7193c0f..8a0bbbd 100644
--- a/plc4go/internal/plc4go/spi/utils/WriteBufferXmlBased.go
+++ b/plc4go/internal/plc4go/spi/utils/WriteBufferXmlBased.go
@@ -20,7 +20,6 @@
 package utils
 
 import (
-       "bufio"
        "encoding/xml"
        "fmt"
        "math/big"
@@ -34,7 +33,7 @@ type WriteBufferXmlBased interface {
 
 func NewXmlWriteBuffer() WriteBufferXmlBased {
        var xmlString strings.Builder
-       encoder := xml.NewEncoder(bufio.NewWriterSize(&xmlString, 1024*16))
+       encoder := xml.NewEncoder(&xmlString)
        encoder.Indent("", "  ")
        return &xmlWriteBuffer{
                xmlString: &xmlString,
@@ -49,6 +48,7 @@ func NewXmlWriteBuffer() WriteBufferXmlBased {
 //
 
 type xmlWriteBuffer struct {
+       bufferCommons
        xmlString *strings.Builder
        *xml.Encoder
 }
@@ -61,114 +61,72 @@ type xmlWriteBuffer struct {
 
 func (x *xmlWriteBuffer) PushContext(logicalName string, _ ...WithWriterArgs) 
error {
        // Pre-emptive flush to avoid overflow when for a long time no context 
gets popped
-       if err := x.Encoder.Flush(); err != nil {
+       if err := x.Flush(); err != nil {
                return err
        }
-       return x.Encoder.EncodeToken(xml.StartElement{Name: xml.Name{Local: 
sanitizeLogicalName(logicalName)}})
+       return x.EncodeToken(xml.StartElement{Name: xml.Name{Local: 
x.sanitizeLogicalName(logicalName)}})
 }
 
 func (x *xmlWriteBuffer) WriteBit(logicalName string, value bool, writerArgs 
...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("bit", 1, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwBitKey, 1, 
writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteUint8(logicalName string, bitLength uint8, value 
uint8, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("uint", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteUint16(logicalName string, bitLength uint8, 
value uint16, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("uint", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteUint32(logicalName string, bitLength uint8, 
value uint32, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("uint", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteUint64(logicalName string, bitLength uint8, 
value uint64, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("uint", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwUintKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteInt8(logicalName string, bitLength uint8, value 
int8, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("int", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteInt16(logicalName string, bitLength uint8, value 
int16, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("int", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteInt32(logicalName string, bitLength uint8, value 
int32, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("int", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteInt64(logicalName string, bitLength uint8, value 
int64, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("int", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteBigInt(logicalName string, bitLength uint8, 
value *big.Int, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("int", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwIntKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteFloat32(logicalName string, bitLength uint8, 
value float32, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("float", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwFloatKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteFloat64(logicalName string, bitLength uint8, 
value float64, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("float", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwFloatKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteBigFloat(logicalName string, bitLength uint8, 
value *big.Float, writerArgs ...WithWriterArgs) error {
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: generateAttr("float", bitLength, writerArgs...),
-       })
+       return x.encodeElement(logicalName, value, x.generateAttr(rwFloatKey, 
bitLength, writerArgs...), writerArgs...)
 }
 
 func (x *xmlWriteBuffer) WriteString(logicalName string, bitLength uint8, 
encoding string, value string, writerArgs ...WithWriterArgs) error {
-       attr := generateAttr("string", bitLength, writerArgs...)
-       attr = append(attr, xml.Attr{Name: xml.Name{Local: "encoding"}, Value: 
encoding})
-       return x.EncodeElement(value, xml.StartElement{
-               Name: xml.Name{Local: sanitizeLogicalName(logicalName)},
-               Attr: attr,
-       })
+       attr := x.generateAttr(rwStringKey, bitLength, writerArgs...)
+       attr = append(attr, xml.Attr{Name: xml.Name{Local: rwEncodingKey}, 
Value: encoding})
+       return x.encodeElement(logicalName, value, attr, writerArgs...)
 }
 
 func (x *xmlWriteBuffer) PopContext(logicalName string, _ ...WithWriterArgs) 
error {
-       if err := x.Encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: 
sanitizeLogicalName(logicalName)}}); err != nil {
+       if err := x.Encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: 
x.sanitizeLogicalName(logicalName)}}); err != nil {
                return err
        }
        return x.Encoder.Flush()
@@ -178,14 +136,21 @@ func (x *xmlWriteBuffer) GetXmlString() string {
        return x.xmlString.String()
 }
 
-func generateAttr(dataType string, bitLength uint8, writerArgs 
...WithWriterArgs) []xml.Attr {
+func (x *xmlWriteBuffer) encodeElement(logicalName string, value interface{}, 
attr []xml.Attr, _ ...WithWriterArgs) error {
+       return x.EncodeElement(value, xml.StartElement{
+               Name: xml.Name{Local: x.sanitizeLogicalName(logicalName)},
+               Attr: attr,
+       })
+}
+
+func (x *xmlWriteBuffer) generateAttr(dataType string, bitLength uint8, 
writerArgs ...WithWriterArgs) []xml.Attr {
        attrs := make([]xml.Attr, 2)
        attrs[0] = xml.Attr{
-               Name:  xml.Name{Local: "dataType"},
+               Name:  xml.Name{Local: rwDataTypeKey},
                Value: dataType,
        }
        attrs[1] = xml.Attr{
-               Name:  xml.Name{Local: "bitLength"},
+               Name:  xml.Name{Local: rwBitLengthKey},
                Value: fmt.Sprintf("%d", bitLength),
        }
        for _, arg := range writerArgs {
@@ -195,17 +160,10 @@ func generateAttr(dataType string, bitLength uint8, 
writerArgs ...WithWriterArgs
                switch arg.(type) {
                case withAdditionalStringRepresentation:
                        attrs = append(attrs, xml.Attr{
-                               Name:  xml.Name{Local: "stringRepresentation"},
+                               Name:  xml.Name{Local: 
rwStringRepresentationKey},
                                Value: 
arg.(withAdditionalStringRepresentation).stringRepresentation,
                        })
                }
        }
        return attrs
 }
-
-func sanitizeLogicalName(logicalName string) string {
-       if logicalName == "" {
-               return "value"
-       }
-       return logicalName
-}
diff --git a/plc4go/internal/plc4go/spi/utils/bufferCommons.go 
b/plc4go/internal/plc4go/spi/utils/bufferCommons.go
new file mode 100644
index 0000000..fcec099
--- /dev/null
+++ b/plc4go/internal/plc4go/spi/utils/bufferCommons.go
@@ -0,0 +1,103 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package utils
+
+import "container/list"
+
+const (
+       rwDataTypeKey             = "dataType"
+       rwBitLengthKey            = "bitLength"
+       rwStringRepresentationKey = "stringRepresentation"
+       rwBitKey                  = "bit"
+       rwUintKey                 = "uint"
+       rwIntKey                  = "int"
+       rwFloatKey                = "float"
+       rwStringKey               = "string"
+       rwEncodingKey             = "encoding"
+)
+
+type bufferCommons struct {
+}
+
+func (b bufferCommons) sanitizeLogicalName(logicalName string) string {
+       if logicalName == "" {
+               return "value"
+       }
+       return logicalName
+}
+
+func (b bufferCommons) isToBeRenderedAsList(readerWriterArgs 
...WithReaderWriterArgs) bool {
+       for _, arg := range readerWriterArgs {
+               if !arg.isWriterArgs() && !arg.isReaderArgs() {
+                       panic("not a reader or writer arg")
+               }
+               switch arg.(type) {
+               case withRenderAsList:
+                       return arg.(withRenderAsList).renderAsList
+               }
+       }
+       return false
+}
+
+func (b bufferCommons) extractAdditionalStringRepresentation(readerWriterArgs 
...WithReaderWriterArgs) string {
+       for _, arg := range readerWriterArgs {
+               if !arg.isWriterArgs() && !arg.isReaderArgs() {
+                       panic("not a reader or writer arg")
+               }
+               switch arg.(type) {
+               case withAdditionalStringRepresentation:
+                       return " " + 
arg.(withAdditionalStringRepresentation).stringRepresentation
+               }
+       }
+       return ""
+}
+
+type stack struct {
+       list.List
+}
+
+func (s *stack) Push(value interface{}) interface{} {
+       s.PushBack(value)
+       return value
+}
+
+func (s *stack) Pop() interface{} {
+       if s.Len() <= 0 {
+               return nil
+       }
+       element := s.Back()
+       if element == nil {
+               return nil
+       }
+       s.Remove(element)
+       return element.Value
+}
+
+func (s *stack) Peek() interface{} {
+       back := s.Back()
+       if back == nil {
+               return nil
+       }
+       return back.Value
+}
+
+func (s stack) Empty() bool {
+       return s.Len() == 0
+}

Reply via email to