You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/01/26 13:03:05 UTC

[plc4x] branch develop updated: refactor(plc4j/codegen): remove IO classes

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 be88a5e  refactor(plc4j/codegen): remove IO classes
be88a5e is described below

commit be88a5e543debab16aafe8ca2c9addcd51bd0f38
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Jan 26 14:02:55 2022 +0100

    refactor(plc4j/codegen): remove IO classes
---
 .../plc4x/language/java/JavaLanguageOutput.java    |   4 +-
 .../resources/templates/java/io-template.java.ftlh | 343 ---------------------
 .../templates/java/model-template.java.ftlh        |  24 +-
 .../org/apache/plc4x/java/abeth/AbEthDriver.java   |   3 +-
 .../org/apache/plc4x/java/ads/ADSPlcDriver.java    |   3 +-
 .../protocol/ads/ManualParserSerializerTest.java   |   3 +-
 .../apache/plc4x/java/bacnetip/BacNetIpDriver.java |   3 +-
 .../plc4x/java/bacnetip/ManualBacNetDecoder.java   |   3 +-
 .../plc4x/java/bacnetip/RandomPackagesTest.java    |   3 +-
 .../canopen/transport/CANOpenFrameDataHandler.java |   4 +-
 .../plc4x/java/canopen/CANOpenPayloadTest.java     |   2 +-
 .../plc4x/java/canopen/CANTestTransport.java       |   4 +-
 .../transport/IdentityCANOpenFrameBuilder.java     |   4 +-
 .../apache/plc4x/java/eip/readwrite/EIPDriver.java |   3 +-
 .../eip/readwrite/protocol/EipProtocolLogic.java   |   5 +-
 .../java/firmata/readwrite/FirmataDriver.java      |   3 +-
 .../apache/plc4x/java/knxnetip/KnxNetIpDriver.java |   3 +-
 .../knxnetip/protocol/KnxNetIpProtocolLogic.java   |   3 +-
 .../org/apache/plc4x/java/modbus/ModbusDriver.java |   3 +-
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |   3 +-
 .../java/opcua/context/EncryptionHandler.java      |   3 +-
 .../plc4x/java/opcua/context/SecureChannel.java    |  33 +-
 .../java/opcua/protocol/OpcuaProtocolLogic.java    |   7 +-
 .../opcua/protocol/OpcuaSubscriptionHandle.java    |   7 +-
 .../apache/plc4x/java/profinet/ProfinetDriver.java |   3 +-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  |   3 +-
 .../profinet/protocol/ProfinetProtocolLogic.java   |   3 +-
 .../apache/plc4x/java/profinet/ProfinetIoTest.java |   1 -
 .../apache/plc4x/java/s7/readwrite/S7Driver.java   |   3 +-
 .../plc4x/java/s7/readwrite/field/S7Field.java     |   3 +-
 .../s7/src/test/java/BenchmarkGeneratedS7.java     |   4 +-
 plc4j/drivers/s7/src/test/java/S7IoTest.java       |   5 +-
 .../connection/SingleProtocolStackConfigurer.java  |  41 ++-
 .../plc4x/java/spi/generation/MessageInput.java    |   1 +
 .../transport/socketcan/SocketCANTransport.java    |   3 +-
 .../plc4x/test/migration/MessageResolver.java      | 153 ++++-----
 .../migration/MessageValidatorAndMigrator.java     |   8 +-
 .../ParserSerializerTestsuiteRunner.java           |   4 +-
 38 files changed, 145 insertions(+), 566 deletions(-)

diff --git a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java
index d6114f1..1b86a1e 100644
--- a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java
+++ b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageOutput.java
@@ -62,9 +62,7 @@ public class JavaLanguageOutput extends FreemarkerLanguageOutput {
 
     @Override
     protected List<Template> getComplexTypeTemplates(Configuration freemarkerConfiguration) throws IOException {
-        return Arrays.asList(
-            freemarkerConfiguration.getTemplate("templates/java/model-template.java.ftlh"),
-            freemarkerConfiguration.getTemplate("templates/java/io-template.java.ftlh"));
+        return List.of(freemarkerConfiguration.getTemplate("templates/java/model-template.java.ftlh"));
     }
 
     @Override
diff --git a/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh
deleted file mode 100644
index 0ec6829..0000000
--- a/code-generation/language-java/src/main/resources/templates/java/io-template.java.ftlh
+++ /dev/null
@@ -1,343 +0,0 @@
-<#--
-  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.
--->
-<#-- Prevent freemarker from escaping stuff -->
-<#outputformat "undefined">
-<#-- Declare the name and type of variables passed in to the template -->
-<#-- @ftlvariable name="languageName" type="java.lang.String" -->
-<#-- @ftlvariable name="protocolName" type="java.lang.String" -->
-<#-- @ftlvariable name="outputFlavor" type="java.lang.String" -->
-<#-- @ftlvariable name="helper" type="org.apache.plc4x.language.java.JavaLanguageTemplateHelper" -->
-<#-- @ftlvariable name="tracer" type="org.apache.plc4x.plugins.codegenerator.protocol.freemarker.Tracer" -->
-<#-- @ftlvariable name="type" type="org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition" -->
-${helper.packageName(protocolName, languageName, outputFlavor)?replace(".", "/")}/io/${type.name}IO.java
-/*
- * 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 ${helper.packageName(protocolName, languageName, outputFlavor)}.io;
-
-import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
-
-import ${helper.packageName(protocolName, languageName, outputFlavor)}.*;
-<#if helper.getComplexTypeReferences()?has_content>import ${helper.packageName(protocolName, languageName, outputFlavor)}.io.*;</#if>
-
-import org.apache.plc4x.java.spi.codegen.fields.*;
-import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
-import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
-import org.apache.plc4x.java.spi.codegen.io.*;
-import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
-import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
-import org.apache.plc4x.java.spi.codegen.WithOption;
-import org.apache.plc4x.java.spi.generation.*;
-import org.apache.plc4x.java.api.value.PlcValue;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.math.BigInteger;
-import java.time.*;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.function.Supplier;
-
-// Code generated by code-generation. DO NOT EDIT.
-
-<#-- TODO: the code below implies that parserArguments will be null if not present... not pretty  -->
-<#if type.parserArguments.isPresent()><#assign parserArguments=type.allParserArguments.orElseThrow()></#if>
-public class ${type.name}IO implements MessageInput<${type.name}> {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(${type.name}IO.class);
-
-<#-- The parse and serialize methods here are just proxies for forwardning the requests to static counterparts -->
-<#if !type.isDiscriminatedChildTypeDefinition()>
-    @Override
-    public ${type.name} parse(ReadBuffer readBuffer, Object... args) throws ParseException {
-        <#if parserArguments?has_content>
-        if((args == null) || (args.length != ${parserArguments?size})) {
-            throw new PlcRuntimeException("Wrong number of arguments, expected ${parserArguments?size}, but got " + args.length);
-        }
-            <#list parserArguments as parserArgument>
-        ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name};
-        if(args[${parserArgument?index}] instanceof ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) {
-            ${parserArgument.name} = (${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) args[${parserArgument?index}];
-            <#if helper.isSimpleTypeReference(parserArgument.type)>
-        } else if (args[${parserArgument?index}] instanceof String) {
-            ${parserArgument.name} = ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}.valueOf((String) args[${parserArgument?index}]);
-            </#if>
-        } else {
-            throw new PlcRuntimeException("Argument ${parserArgument?index} expected to be of type ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} or a string which is parseable but was " + args[${parserArgument?index}].getClass().getName());
-        }
-            </#list>
-        </#if>
-        return ${type.name}IO.staticParse(readBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
-    }
-
-<#else>
-    @Override
-    public ${type.name} parse(ReadBuffer readBuffer, Object... args) throws ParseException {
-        return (${type.name}) new ${type.parentType.name}IO().parse(readBuffer, args);
-    }
-
-</#if>
-<#-- Here come the actual parse and serialize methods that actually do the parsing and serlaizing -->
-    <#assign hasParserArguments=parserArguments?has_content/>
-    <#assign parserArgumentList><#if hasParserArguments><#list parserArguments as parserArgument>${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
-    public static ${type.name}<#if type.isDiscriminatedChildTypeDefinition()>Builder</#if> staticParse(ReadBuffer readBuffer<#if hasParserArguments>, ${parserArgumentList}</#if>) throws ParseException {
-        readBuffer.pullContext("${type.name}");
-        int startPos = readBuffer.getPos();
-        int curPos;
-<#list type.fields as field>
-<#switch field.typeName>
-    <#case "array">
-        <#assign arrayField = field.asArrayField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        <#if typedField.type.isByteBased()>
-            <#if !field.isCountArrayField() && !field.isLengthArrayField()>
-        throw new ParseException("array fields of type byte only support 'count' and 'length' loop-types.");
-            </#if>
-        byte[] ${namedField.name} = readBuffer.readByteArray("${namedField.name}", (int) ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#else>
-        <#-- If this is a count array, we can directly initialize an array with the given size -->
-            <#if field.isCountArrayField()>
-        List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readCountArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#-- In all other cases do we have to work with a list, that is later converted to an array -->
-            <#else>
-            <#-- For a length array, we read data till the read position of the buffer reaches a given position -->
-                <#if field.isLengthArrayField()>
-        List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readLengthArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, ${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
-                <#elseif field.isTerminatedArrayField()>
-        List<${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)}> ${arrayField.name} = readTerminatedArrayField("${arrayField.name}", ${helper.getDataReaderCall(arrayField.type)}, () -> ((boolean) (${helper.toParseExpression(arrayField, helper.intTypeReference, arrayField.loopExpression, parserArguments)}))${helper.getFieldOptions(typedField, parserArguments)});
-                </#if>
-            </#if>
-        </#if>
-        <#break>
-    <#case "assert">
-        <#assign assertField = field.asAssertField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${helper.toParseExpression(assertField, assertField.type, assertField.conditionExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "checksum">
-        <#assign checksumField = field.asChecksumField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "const">
-        <#assign constField = field.asConstField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}, ${type.name}.${namedField.name?upper_case}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "discriminator">
-        <#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "enum">
-        <#assign enumField = field.asEnumField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", "${helper.getTypeDefinitionForTypeReference(enumField.type).name}", readEnum(${helper.getTypeDefinitionForTypeReference(enumField.type).name}::firstEnumForField${enumField.fieldName?cap_first}, ${helper.getDataReaderCall(helper.getEnumFieldTypeReference(enumField.type, enumField.fieldName))})${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "implicit">
-        <#assign implicitField = field.asImplicitField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "manualArray">
-        <#assign manualArrayField = field.asManualArrayField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        <#if manualArrayField.type.isByteBased()>
-        byte[] ${namedField.name} = readManualByteArrayField("${namedField.name}", readBuffer, (byte[] _values) -> (boolean) (${helper.toParseExpression(manualArrayField, helper.boolTypeReference, manualArrayField.loopExpression, parserArguments)}), () -> (${helper.getLanguageTypeNameForField(manualArrayField)}) (${helper.toParseExpression(manualArrayField, manualArrayField.type, manualArrayField.parseExpression, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)});
-        <#else>
-        List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> ${namedField.name} = readManualArrayField("${namedField.name}", readBuffer, (List<${helper.getNonPrimitiveLanguageTypeNameForField(manualArrayField)}> _values) -> (boolean) (${helper.toParseExpression(manualArrayField, helper.boolTypeReference, manualArrayField.loopExpression, parserArguments)}), () -> (${helper.getLanguageTypeNameForField(manualArrayField)}) (${helper.toParseExpression(manualArrayField, ma [...]
-        </#if>
-        <#break>
-    <#case "manual">
-        <#assign manualField = field.asManualField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${manualField.name} = readManualField("${namedField.name}", readBuffer, () -> (${helper.getLanguageTypeNameForField(manualField)}) (${helper.toParseExpression(manualField, manualField.type, manualField.parseExpression, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "optional">
-        <#assign optionalField = field.asOptionalField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}<#if optionalField.conditionExpression.present>, ${helper.toParseExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}</#if>${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "padding">
-        <#assign paddingField = field.asPaddingField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>
-
-        read${field.typeName?cap_first}Field(${helper.getDataReaderCall(typedField.type)}, (int) (${helper.toParseExpression(paddingField, paddingField.type, paddingField.paddingCondition, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "reserved">
-        <#assign reservedField = field.asReservedField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-
-        read${field.typeName?cap_first}Field("reserved", ${helper.getDataReaderCall(typedField.type)}, ${helper.getReservedValue(reservedField)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "simple">
-        <#assign simpleField = field.asSimpleField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = <#if helper.isEnumTypeReference(typedField.type)>readEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});<#else>read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});</#if>
-        <#break>
-    <#case "switch">
-        <#assign switchField = field.asSwitchField().orElseThrow()>
-
-        // Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
-        ${type.name}Builder builder = null;
-        <#list switchField.cases as case>
-            <@compress single_line=true>
-            <#if case.discriminatorValueTerms?has_content>
-                if(
-                <#list case.discriminatorValueTerms as discriminatorValueTerm>
-                    <#if helper.isWildcard(discriminatorValueTerm)>
-                        true
-                    <#else>
-                        <#assign discriminatorExpression=switchField.discriminatorExpressions[discriminatorValueTerm?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
-                        <#assign discriminatorType=helper.getDiscriminatorTypes()[discriminatorExpression.discriminatorName]>
-                        EvaluationHelper.equals(
-                        ${helper.toParseExpression(switchField, discriminatorType, discriminatorExpression, parserArguments)},
-                        <#if helper.isEnumTypeReference(discriminatorType)>
-                        ${helper.getLanguageTypeNameForTypeReference(discriminatorType)}.${helper.toParseExpression(switchField, discriminatorType, discriminatorValueTerm, parserArguments)}
-                        <#else>
-                        ${helper.toParseExpression(switchField, discriminatorType, discriminatorValueTerm, parserArguments)}
-                        </#if>
-                        )
-                    </#if>
-                    <#sep> && </#sep>
-                </#list>
-                )
-            </#if>{
-            </...@compress>
-            <@compress single_line=true>
-            <#assign hasCaseParseArguments=case.allParserArguments.isPresent() && case.allParserArguments.orElseThrow()?has_content>
-            <#assign caseParseArguments><#if hasCaseParseArguments><#list case.allParserArguments.orElseThrow() as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if></#assign>
-            builder = ${case.name}IO.staticParse(readBuffer<#if hasCaseParseArguments>, ${tracer.dive("case parse arguments")} ${caseParseArguments}</#if>);
-            </...@compress>
-        }<#sep> else </#sep>
-        </#list>
-        if (builder == null) {
-            throw new ParseException("Unsupported case for discriminated type");
-        }
-        <#break>
-    <#case "unknown">
-        <#assign unknownField = field.asUnknownField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-
-        read${field.typeName?cap_first}Field("unknown", ${helper.getDataReaderCall(typedField.type)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "virtual">
-        <#assign virtualField = field.asVirtualField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getLanguageTypeNameForField(field)}.class, ${helper.toParseExpression(virtualField, virtualField.type, virtualField.valueExpression, parserArguments)}${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-    <#case "validation">
-        <#assign validationField = field.asValidationField().orElseThrow()>
-        // Validation
-        if (!(${helper.toParseExpression(validationField, helper.boolTypeReference, validationField.getValidationExpression(), null)})) {
-            throw new ParseAssertException(${validationField.getDescription().orElse("Validation failed")});
-        }
-        <#break>
-    <#case "peek">
-        <#assign peekField = field.asPeekField().orElseThrow()>
-        <#assign typedField = field.asTypedField().orElseThrow()>
-        <#assign namedField = field.asNamedField().orElseThrow()>
-
-        ${helper.getLanguageTypeNameForField(field)} ${namedField.name} = read${field.typeName?cap_first}Field("${namedField.name}", ${helper.getDataReaderCall(typedField.type)}<#if peekField.offsetExpression.present>, ${helper.toParseExpression(peekField, helper.boolTypeReference, peekField.offsetExpression.get(), parserArguments)}</#if>${helper.getFieldOptions(typedField, parserArguments)});
-        <#break>
-</#switch>
-</#list>
-
-        readBuffer.closeContext("${type.name}");
-        // Create the instance
-        <#if type.isDiscriminatedChildTypeDefinition()>
-        return new ${type.name}Builder(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
-        <#elseif type.isDiscriminatedParentTypeDefinition()>
-        return builder.build(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
-        <#else>
-        return new ${type.name}(<#list type.propertyFields as field>${field.name}<#sep>, </#sep></#list>);
-        </#if>
-    }
-
-<#if type.isDiscriminatedParentTypeDefinition()>
-    public static interface ${type.name}Builder {
-        ${type.name} build(<#list type.propertyFields as field><#if field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>);
-    }
-
-</#if>
-<#if type.isDiscriminatedChildTypeDefinition()>
-    public static class ${type.name}Builder implements ${type.parentType.name}IO.${type.parentType.name}Builder {
-        <#if type.propertyFields?has_content>
-            <#list type.propertyFields as field>
-        private final <#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name};
-            </#list>
-        </#if>
-
-        public ${type.name}Builder(<#list type.propertyFields as field><#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>) {
-            <#list type.propertyFields as field>
-            this.${field.name} = ${field.name};
-            </#list>
-        }
-
-        public ${type.name} build(<#list type.parentType.asComplexTypeDefinition().orElseThrow().propertyFields as field><#if field.loopType?? && field.type.isByteBased()>byte[]<#elseif field.loopType??>List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}><#else>${helper.getLanguageTypeNameForField(field)}</#if> ${field.name}<#sep>, </#sep></#list>) {
-            return new ${type.name}(<#list type.allPropertyFields as field>${field.name}<#sep>, </#sep></#list>);
-        }
-    }
-
-</#if>
-}
-</#outputformat>
diff --git a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
index 27de56b..35c9f59 100644
--- a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
+++ b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
@@ -68,7 +68,7 @@ import java.math.BigInteger;
 
 <#-- TODO: the code below implies that parserArguments will be null if not present... not pretty  -->
 <#if type.parserArguments.isPresent()><#assign parserArguments=type.allParserArguments.orElseThrow()></#if>
-public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()><T extends ${type.name}></#if><#if type.parentType??> extends ${type.parentType.name}<${type.name}></#if> implements Message, MessageInput<<#if type.isDiscriminatedParentTypeDefinition()>T<#else>${type.name}</#if>> {
+public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()><T extends ${type.name}></#if><#if type.parentType??> extends ${type.parentType.name}<${type.name}></#if> implements Message {
 
 <#--
     If this is a discriminated child type, we need to generate methods for accessing it's discriminator
@@ -481,32 +481,26 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
 
 <#-- The parse and serialize methods here are just proxies for forwardning the requests to static counterparts -->
     <#if !type.isDiscriminatedChildTypeDefinition()>
-    @Override
-    public <#if type.isDiscriminatedParentTypeDefinition()>T<#else>${type.name}</#if> parse(ReadBuffer readBuffer, Object... args) throws ParseException {
+    public static ${type.name} staticParse(ReadBuffer readBuffer, Object... args) throws ParseException {
         <#if parserArguments?has_content>
         if((args == null) || (args.length != ${parserArguments?size})) {
             throw new PlcRuntimeException("Wrong number of arguments, expected ${parserArguments?size}, but got " + args.length);
         }
             <#list parserArguments as parserArgument>
-                ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} ${parserArgument.name};
-        if(args[${parserArgument?index}] instanceof ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) {
-            ${parserArgument.name} = (${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}) args[${parserArgument?index}];
+                <#assign languageName=helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)>
+                ${languageName} ${parserArgument.name};
+        if(args[${parserArgument?index}] instanceof ${languageName}) {
+            ${parserArgument.name} = (${languageName}) args[${parserArgument?index}];
                 <#if helper.isSimpleTypeReference(parserArgument.type)>
         } else if (args[${parserArgument?index}] instanceof String) {
-            ${parserArgument.name} = ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}.valueOf((String) args[${parserArgument?index}]);
+            ${parserArgument.name} = ${languageName}.valueOf((String) args[${parserArgument?index}]);
                 </#if>
         } else {
-            throw new PlcRuntimeException("Argument ${parserArgument?index} expected to be of type ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)} or a string which is parseable but was " + args[${parserArgument?index}].getClass().getName());
+            throw new PlcRuntimeException("Argument ${parserArgument?index} expected to be of type ${languageName} or a string which is parseable but was " + args[${parserArgument?index}].getClass().getName());
         }
             </#list>
         </#if>
-        return <#if type.isDiscriminatedParentTypeDefinition()>(T)</#if>${type.name}.staticParse(readBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
-    }
-
-    <#else>
-    @Override
-    public ${type.name} parse(ReadBuffer readBuffer, Object... args) throws ParseException {
-        return (${type.name}) super.parse(readBuffer, args);
+        return staticParse(readBuffer<#if parserArguments?has_content>, <#list parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
     }
 
     </#if>
diff --git a/plc4j/drivers/ab-eth/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java b/plc4j/drivers/ab-eth/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java
index 58c5fb8..c255613 100644
--- a/plc4j/drivers/ab-eth/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java
+++ b/plc4j/drivers/ab-eth/src/main/java/org/apache/plc4x/java/abeth/AbEthDriver.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.abeth.field.AbEthField;
 import org.apache.plc4x.java.abeth.field.AbEthFieldHandler;
 import org.apache.plc4x.java.abeth.protocol.AbEthProtocolLogic;
 import org.apache.plc4x.java.abeth.readwrite.CIPEncapsulationPacket;
-import org.apache.plc4x.java.abeth.readwrite.io.CIPEncapsulationPacketIO;
 import org.apache.plc4x.java.api.model.PlcField;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
@@ -81,7 +80,7 @@ public class AbEthDriver extends GeneratedDriverBase<CIPEncapsulationPacket> {
 
     @Override
     protected ProtocolStackConfigurer<CIPEncapsulationPacket> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(CIPEncapsulationPacket.class, CIPEncapsulationPacketIO.class)
+        return SingleProtocolStackConfigurer.builder(CIPEncapsulationPacket.class, CIPEncapsulationPacket::staticParse)
             .withProtocol(AbEthProtocolLogic.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             .build();
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
index adaf58e..10afd79 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
@@ -23,7 +23,6 @@ import org.apache.plc4x.java.ads.configuration.AdsConfiguration;
 import org.apache.plc4x.java.ads.field.AdsFieldHandler;
 import org.apache.plc4x.java.ads.protocol.AdsProtocolLogic;
 import org.apache.plc4x.java.ads.readwrite.AmsTCPPacket;
-import org.apache.plc4x.java.ads.readwrite.io.AmsTCPPacketIO;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
 import org.apache.plc4x.java.spi.configuration.Configuration;
@@ -99,7 +98,7 @@ public class ADSPlcDriver extends GeneratedDriverBase<AmsTCPPacket> {
 
     @Override
     protected ProtocolStackConfigurer<AmsTCPPacket> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(AmsTCPPacket.class, AmsTCPPacketIO.class)
+        return SingleProtocolStackConfigurer.builder(AmsTCPPacket.class, AmsTCPPacket::staticParse)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             .withProtocol(AdsProtocolLogic.class)
             .littleEndian()
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
index 14ab1be..fd41816 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
@@ -20,7 +20,6 @@ package org.apache.plc4x.protocol.ads;
 
 import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.ads.readwrite.AmsTCPPacket;
-import org.apache.plc4x.java.ads.readwrite.io.AmsTCPPacketIO;
 import org.apache.plc4x.java.spi.generation.ByteOrder;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
@@ -31,7 +30,7 @@ public class ManualParserSerializerTest {
         byte[] data = Hex.decodeHex("0000f1000000c0a817c80101feffc0a817140101530309000500d1000000000000002f00000000000000c90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012a00a07d0f7e1c8e31489f8cf1006b3604b888defc88f64eb6fbb217d11fc5cf5f148b0abf05407c941a93b8263301db0f4940d66875727a0000000000d2040000ab6459032bbf7e03b888defcb8a5b249044de82ee03a2ab [...]
         //byte[] data = Hex.decodeHex("0000f1000000c0a817c80101feffc0a817140101530309000500d1000000000000002f00000000000000c90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012a00a07d0f7e1c8e31489f8cf1006b3604b888defc");
         ReadBuffer readBuffer = new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN);
-        final AmsTCPPacket amsTCPPacket = AmsTCPPacketIO.staticParse(readBuffer);
+        final AmsTCPPacket amsTCPPacket = AmsTCPPacket.staticParse(readBuffer);
         System.out.println(amsTCPPacket);
     }
 
diff --git a/plc4j/drivers/bacnet/src/main/java/org/apache/plc4x/java/bacnetip/BacNetIpDriver.java b/plc4j/drivers/bacnet/src/main/java/org/apache/plc4x/java/bacnetip/BacNetIpDriver.java
index 0c8b5ec..c725f82 100644
--- a/plc4j/drivers/bacnet/src/main/java/org/apache/plc4x/java/bacnetip/BacNetIpDriver.java
+++ b/plc4j/drivers/bacnet/src/main/java/org/apache/plc4x/java/bacnetip/BacNetIpDriver.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.bacnetip.configuration.BacNetIpConfiguration;
 import org.apache.plc4x.java.bacnetip.field.BacNetIpFieldHandler;
 import org.apache.plc4x.java.bacnetip.protocol.BacNetIpProtocolLogic;
 import org.apache.plc4x.java.bacnetip.readwrite.BVLC;
-import org.apache.plc4x.java.bacnetip.readwrite.io.BVLCIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
@@ -85,7 +84,7 @@ public class BacNetIpDriver extends GeneratedDriverBase<BVLC> {
 
     @Override
     protected ProtocolStackConfigurer<BVLC> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(BVLC.class, BVLCIO.class)
+        return SingleProtocolStackConfigurer.builder(BVLC.class, BVLC::staticParse)
             .withProtocol(BacNetIpProtocolLogic.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             .withCorruptPacketRemover(CorruptPackageCleaner.class)
diff --git a/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/ManualBacNetDecoder.java b/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/ManualBacNetDecoder.java
index b9d48b9..7aba82b 100644
--- a/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/ManualBacNetDecoder.java
+++ b/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/ManualBacNetDecoder.java
@@ -20,7 +20,6 @@ package org.apache.plc4x.java.bacnetip;
 
 import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.bacnetip.readwrite.BVLC;
-import org.apache.plc4x.java.bacnetip.readwrite.io.BVLCIO;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 
@@ -29,7 +28,7 @@ public class ManualBacNetDecoder {
     public static void main(String[] args) throws Exception {
         final byte[] bytes = Hex.decodeHex("810a002b01040205790109011c020000142c000002f93a06b24e09552e44434a00002f096f2e8204002f4f");
         ReadBuffer readBuffer = new ReadBufferByteBased(bytes);
-        final BVLC packet = BVLCIO.staticParse(readBuffer);
+        final BVLC packet = BVLC.staticParse(readBuffer);
         System.out.println(packet);
     }
 
diff --git a/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/RandomPackagesTest.java b/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/RandomPackagesTest.java
index 358e115..90754b2 100644
--- a/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/RandomPackagesTest.java
+++ b/plc4j/drivers/bacnet/src/test/java/org/apache/plc4x/java/bacnetip/RandomPackagesTest.java
@@ -24,7 +24,6 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.SystemUtils;
 import org.apache.plc4x.java.bacnetip.readwrite.*;
-import org.apache.plc4x.java.bacnetip.readwrite.io.BVLCIO;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 import org.apache.plc4x.java.spi.generation.SerializationException;
@@ -5165,7 +5164,7 @@ public class RandomPackagesTest {
             byte[] rawData = udpPacket.getPayload().getRawData();
             LOGGER.info("Reading BVLC from:\n{}", Hex.dump(rawData));
             try {
-                return BVLCIO.staticParse(new ReadBufferByteBased(rawData));
+                return BVLC.staticParse(new ReadBufferByteBased(rawData));
             } catch (ParseException e) {
                 throw new ParseException(String.format("Caught at current package number: %d. Packages read so far %d", currentPackageNumber, readPackages), e);
             }
diff --git a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/transport/CANOpenFrameDataHandler.java b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/transport/CANOpenFrameDataHandler.java
index 457c92d..d7b8cd3 100644
--- a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/transport/CANOpenFrameDataHandler.java
+++ b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/transport/CANOpenFrameDataHandler.java
@@ -24,10 +24,8 @@ import org.apache.plc4x.java.canopen.readwrite.utils.StaticHelper;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenFrame;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenService;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.spi.generation.ByteOrder;
 import org.apache.plc4x.java.spi.generation.Message;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
 import org.apache.plc4x.java.transport.can.CANFrameBuilder;
@@ -50,7 +48,7 @@ public class CANOpenFrameDataHandler implements FrameHandler<Message, CANOpenFra
     public CANOpenFrame fromCAN(FrameData frame) {
         CANOpenService service = StaticHelper.serviceId((short) frame.getNodeId());
         int nodeId = Math.abs(service.getMin() - frame.getNodeId());
-        return new CANOpenFrame((short) nodeId, service, frame.read(new CANOpenPayloadIO(), service));
+        return new CANOpenFrame((short) nodeId, service, frame.read(CANOpenPayload::staticParse, service));
     }
 
     @Override
diff --git a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenPayloadTest.java b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenPayloadTest.java
index 31eb9e7..3d99462 100644
--- a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenPayloadTest.java
+++ b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANOpenPayloadTest.java
@@ -23,7 +23,7 @@ import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
 public class CANOpenPayloadTest extends ParserSerializerTestsuiteRunner {
 
     public CANOpenPayloadTest() {
-        super("/protocols/canopen/CANOpenPayloadTestSuite.xml", true);
+        super("/protocols/canopen/CANOpenPayloadTestSuite.xml");
     }
 
 }
diff --git a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANTestTransport.java b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANTestTransport.java
index 7c16521..b1d9313 100644
--- a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANTestTransport.java
+++ b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/CANTestTransport.java
@@ -24,11 +24,9 @@ import java.util.function.Function;
 import java.util.function.ToIntFunction;
 
 import org.apache.plc4x.java.canopen.readwrite.CANOpenFrame;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenFrameIO;
 import org.apache.plc4x.java.canopen.transport.CANOpenFrameDataAdapter;
 import org.apache.plc4x.java.canopen.transport.IdentityCANOpenFrameBuilder;
 import org.apache.plc4x.java.spi.configuration.Configuration;
-import org.apache.plc4x.java.spi.generation.MessageIO;
 import org.apache.plc4x.java.spi.generation.MessageInput;
 import org.apache.plc4x.java.transport.can.CANFrameBuilder;
 import org.apache.plc4x.java.transport.can.CANTransport;
@@ -49,7 +47,7 @@ public class CANTestTransport extends TestTransport implements CANTransport<CANO
 
     @Override
     public MessageInput<CANOpenFrame> getMessageInput(Configuration configuration) {
-        return new CANOpenFrameIO();
+        return CANOpenFrame::staticParse;
     }
 
     @Override
diff --git a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/transport/IdentityCANOpenFrameBuilder.java b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/transport/IdentityCANOpenFrameBuilder.java
index e68329f..d7208ad 100644
--- a/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/transport/IdentityCANOpenFrameBuilder.java
+++ b/plc4j/drivers/canopen/src/test/java/org/apache/plc4x/java/canopen/transport/IdentityCANOpenFrameBuilder.java
@@ -19,10 +19,10 @@ under the License.
 package org.apache.plc4x.java.canopen.transport;
 
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.canopen.readwrite.CANOpenPayload;
 import org.apache.plc4x.java.canopen.readwrite.utils.StaticHelper;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenFrame;
 import org.apache.plc4x.java.canopen.readwrite.CANOpenService;
-import org.apache.plc4x.java.canopen.readwrite.io.CANOpenPayloadIO;
 import org.apache.plc4x.java.spi.generation.ByteOrder;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
@@ -56,7 +56,7 @@ public class IdentityCANOpenFrameBuilder implements CANFrameBuilder<CANOpenFrame
     public CANOpenFrame create() {
         try {
             return new CANOpenFrame(
-                nodeId, service, CANOpenPayloadIO.staticParse(new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN), service)
+                nodeId, service, CANOpenPayload.staticParse(new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN), service)
             );
         } catch (ParseException e) {
             throw new PlcRuntimeException(e);
diff --git a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/EIPDriver.java b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/EIPDriver.java
index 3fcb65d..57f0f31 100644
--- a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/EIPDriver.java
+++ b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/EIPDriver.java
@@ -22,7 +22,6 @@ import io.netty.buffer.ByteBuf;
 import org.apache.plc4x.java.eip.readwrite.configuration.EIPConfiguration;
 import org.apache.plc4x.java.eip.readwrite.field.EipField;
 import org.apache.plc4x.java.eip.readwrite.field.EipFieldHandler;
-import org.apache.plc4x.java.eip.readwrite.io.EipPacketIO;
 import org.apache.plc4x.java.eip.readwrite.protocol.EipProtocolLogic;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
@@ -88,7 +87,7 @@ public class EIPDriver extends GeneratedDriverBase<EipPacket> {
 
     @Override
     protected ProtocolStackConfigurer<EipPacket> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(EipPacket.class, EipPacketIO.class)
+        return SingleProtocolStackConfigurer.builder(EipPacket.class, EipPacket::staticParse)
             .withProtocol(EipProtocolLogic.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             .littleEndian()
diff --git a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/protocol/EipProtocolLogic.java b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/protocol/EipProtocolLogic.java
index 4d6b8e4..66c1651 100644
--- a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/protocol/EipProtocolLogic.java
+++ b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/readwrite/protocol/EipProtocolLogic.java
@@ -28,7 +28,6 @@ import org.apache.plc4x.java.api.value.*;
 import org.apache.plc4x.java.eip.readwrite.*;
 import org.apache.plc4x.java.eip.readwrite.configuration.EIPConfiguration;
 import org.apache.plc4x.java.eip.readwrite.field.EipField;
-import org.apache.plc4x.java.eip.readwrite.io.CipServiceIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
@@ -292,7 +291,7 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
                 ReadBuffer serviceBuf = new ReadBufferByteBased(read.getBytes(offset, offset + length), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
                 CipService service = null;
                 try {
-                    service = CipServiceIO.staticParse(read, length);
+                    service = CipService.staticParse(read, length);
                     arr.add(service);
                 } catch (ParseException e) {
                     throw new PlcRuntimeException(e);
@@ -503,7 +502,7 @@ public class EipProtocolLogic extends Plc4xProtocolBase<EipPacket> implements Ha
                 ReadBuffer serviceBuf = new ReadBufferByteBased(read.getBytes(offset, length), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
                 CipService service = null;
                 try {
-                    service = CipServiceIO.staticParse(read, length);
+                    service = CipService.staticParse(read, length);
                     arr.add(service);
                 } catch (ParseException e) {
                     throw new PlcRuntimeException(e);
diff --git a/plc4j/drivers/firmata/src/main/java/org/apache/plc4x/java/firmata/readwrite/FirmataDriver.java b/plc4j/drivers/firmata/src/main/java/org/apache/plc4x/java/firmata/readwrite/FirmataDriver.java
index a7707bc..1530f25 100644
--- a/plc4j/drivers/firmata/src/main/java/org/apache/plc4x/java/firmata/readwrite/FirmataDriver.java
+++ b/plc4j/drivers/firmata/src/main/java/org/apache/plc4x/java/firmata/readwrite/FirmataDriver.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.firmata.readwrite.configuration.FirmataConfiguratio
 import org.apache.plc4x.java.firmata.readwrite.context.FirmataDriverContext;
 import org.apache.plc4x.java.firmata.readwrite.field.FirmataField;
 import org.apache.plc4x.java.firmata.readwrite.field.FirmataFieldHandler;
-import org.apache.plc4x.java.firmata.readwrite.io.FirmataMessageIO;
 import org.apache.plc4x.java.firmata.readwrite.protocol.FirmataProtocolLogic;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
@@ -90,7 +89,7 @@ public class FirmataDriver extends GeneratedDriverBase<FirmataMessage> {
 
     @Override
     protected ProtocolStackConfigurer<FirmataMessage> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(FirmataMessage.class, FirmataMessageIO.class)
+        return SingleProtocolStackConfigurer.builder(FirmataMessage.class, FirmataMessage::staticParse)
             .withProtocol(FirmataProtocolLogic.class)
             .withDriverContext(FirmataDriverContext.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
diff --git a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
index 01b76e0..1646ac8 100644
--- a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
+++ b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/KnxNetIpDriver.java
@@ -23,7 +23,6 @@ import org.apache.plc4x.java.knxnetip.configuration.KnxNetIpConfiguration;
 import org.apache.plc4x.java.knxnetip.context.KnxNetIpDriverContext;
 import org.apache.plc4x.java.knxnetip.field.KnxNetIpField;
 import org.apache.plc4x.java.knxnetip.readwrite.KnxNetIpMessage;
-import org.apache.plc4x.java.knxnetip.readwrite.io.KnxNetIpMessageIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.knxnetip.field.KnxNetIpFieldHandler;
 import org.apache.plc4x.java.knxnetip.protocol.KnxNetIpProtocolLogic;
@@ -97,7 +96,7 @@ public class KnxNetIpDriver extends GeneratedDriverBase<KnxNetIpMessage> {
 
     @Override
     protected ProtocolStackConfigurer<KnxNetIpMessage> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(KnxNetIpMessage.class, KnxNetIpMessageIO.class)
+        return SingleProtocolStackConfigurer.builder(KnxNetIpMessage.class, KnxNetIpMessage::staticParse)
             .withProtocol(KnxNetIpProtocolLogic.class)
             .withDriverContext(KnxNetIpDriverContext.class)
             .withPacketSizeEstimator(PacketSizeEstimator.class)
diff --git a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
index eeb836e..d9b3ed9 100644
--- a/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
+++ b/plc4j/drivers/knxnetip/src/main/java/org/apache/plc4x/java/knxnetip/protocol/KnxNetIpProtocolLogic.java
@@ -35,7 +35,6 @@ import org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress;
 import org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress2Level;
 import org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress3Level;
 import org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddressFreeLevel;
-import org.apache.plc4x.java.knxnetip.readwrite.io.KnxGroupAddressIO;
 import org.apache.plc4x.java.knxnetip.readwrite.io.KnxDatapointIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
@@ -452,7 +451,7 @@ public class KnxNetIpProtocolLogic extends Plc4xProtocolBase<KnxNetIpMessage> im
         // Decode the group address depending on the project settings.
         ReadBuffer addressBuffer = new ReadBufferByteBased(destinationGroupAddress);
         final KnxGroupAddress knxGroupAddress =
-            KnxGroupAddressIO.staticParse(addressBuffer, knxNetIpDriverContext.getGroupAddressType());
+            KnxGroupAddress.staticParse(addressBuffer, knxNetIpDriverContext.getGroupAddressType());
         final String destinationAddress = toString(knxGroupAddress);
 
         // If there is an ETS5 model provided, continue decoding the payload.
diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
index 94a440e..8dba7e3 100644
--- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
+++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.modbus.field.ModbusField;
 import org.apache.plc4x.java.modbus.field.ModbusFieldHandler;
 import org.apache.plc4x.java.modbus.protocol.ModbusProtocolLogic;
 import org.apache.plc4x.java.modbus.readwrite.ModbusTcpADU;
-import org.apache.plc4x.java.modbus.readwrite.io.ModbusTcpADUIO;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
 import org.apache.plc4x.java.spi.configuration.Configuration;
@@ -103,7 +102,7 @@ public class ModbusDriver extends GeneratedDriverBase<ModbusTcpADU> {
 
     @Override
     protected ProtocolStackConfigurer<ModbusTcpADU> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(ModbusTcpADU.class, ModbusTcpADUIO.class)
+        return SingleProtocolStackConfigurer.builder(ModbusTcpADU.class, ModbusTcpADU::staticParse)
             .withProtocol(ModbusProtocolLogic.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             // Every incoming message is to be treated as a response.
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
index 4654c73..f3d9793 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
@@ -27,7 +27,6 @@ import org.apache.plc4x.java.opcua.optimizer.OpcuaOptimizer;
 import org.apache.plc4x.java.opcua.protocol.*;
 import org.apache.plc4x.java.opcua.config.*;
 import org.apache.plc4x.java.opcua.readwrite.*;
-import org.apache.plc4x.java.opcua.readwrite.io.*;
 import org.apache.plc4x.java.spi.configuration.ConfigurationFactory;
 import org.apache.plc4x.java.spi.connection.*;
 import org.apache.plc4x.java.spi.transport.Transport;
@@ -125,7 +124,7 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
 
     @Override
     protected ProtocolStackConfigurer<OpcuaAPU> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(OpcuaAPU.class, OpcuaAPUIO.class)
+        return SingleProtocolStackConfigurer.builder(OpcuaAPU.class, OpcuaAPU::staticParse)
             .withProtocol(OpcuaProtocolLogic.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
             .withParserArgs(true)
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
index ab27f25..dca52b8 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/EncryptionHandler.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.opcua.readwrite.MessagePDU;
 import org.apache.plc4x.java.opcua.readwrite.OpcuaAPU;
 import org.apache.plc4x.java.opcua.readwrite.OpcuaMessageResponse;
 import org.apache.plc4x.java.opcua.readwrite.OpcuaOpenResponse;
-import org.apache.plc4x.java.opcua.readwrite.io.OpcuaAPUIO;
 import org.apache.plc4x.java.spi.generation.*;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.slf4j.Logger;
@@ -139,7 +138,7 @@ public class EncryptionHandler {
                     buf.setPos(4);
                     buf.writeInt(32, tempPos - 256);
                     ReadBuffer readBuffer = new ReadBufferByteBased(getBytes(buf.getBytes(), 0, tempPos - 256), ByteOrder.LITTLE_ENDIAN);
-                    return OpcuaAPUIO.staticParse(readBuffer, true);
+                    return OpcuaAPU.staticParse(readBuffer, true);
                 } catch (SerializationException | ParseException e) {
                     LOGGER.error("Unable to Parse encrypted message");
                 }
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java
index df8bd60..262a927 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/SecureChannel.java
@@ -24,8 +24,6 @@ import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.opcua.config.OpcuaConfiguration;
 import org.apache.plc4x.java.opcua.readwrite.*;
-import org.apache.plc4x.java.opcua.readwrite.io.ExtensionObjectIO;
-import org.apache.plc4x.java.opcua.readwrite.io.OpcuaAPUIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.context.DriverContext;
 import org.apache.plc4x.java.spi.generation.*;
@@ -43,10 +41,6 @@ import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateEncodingException;
 import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeoutException;
@@ -56,7 +50,6 @@ import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 public class SecureChannel {
@@ -193,7 +186,7 @@ public class SecureChannel {
         final OpcuaAPU apu;
         try {
             if (this.isEncrypted) {
-                apu = OpcuaAPUIO.staticParse(encryptionHandler.encodeMessage(messageRequest, buffer.getData()), false);
+                apu = OpcuaAPU.staticParse(encryptionHandler.encodeMessage(messageRequest, buffer.getData()), false);
             } else {
                 apu = new OpcuaAPU(messageRequest);
             }
@@ -324,7 +317,7 @@ public class SecureChannel {
             final OpcuaAPU apu;
 
             if (this.isEncrypted) {
-                apu = OpcuaAPUIO.staticParse(encryptionHandler.encodeMessage(openRequest, buffer.getData()), false);
+                apu = OpcuaAPU.staticParse(encryptionHandler.encodeMessage(openRequest, buffer.getData()), false);
             } else {
                 apu = new OpcuaAPU(openRequest);
             }
@@ -338,7 +331,7 @@ public class SecureChannel {
                 .handle(opcuaOpenResponse -> {
                     try {
                         ReadBuffer readBuffer = new ReadBufferByteBased(opcuaOpenResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
-                        ExtensionObject message = ExtensionObjectIO.staticParse(readBuffer, false);
+                        ExtensionObject message = ExtensionObject.staticParse(readBuffer, false);
                         //Store the initial sequence number from the server. there's no requirement for the server and client to use the same starting number.
                         senderSequenceNumber.set(opcuaOpenResponse.getSequenceNumber());
 
@@ -424,7 +417,7 @@ public class SecureChannel {
 
             Consumer<byte[]> consumer = opcuaResponse -> {
                 try {
-                    ExtensionObject message = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
+                    ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
                     if (message.getBody() instanceof ServiceFault) {
                         ServiceFault fault = (ServiceFault) message.getBody();
                         LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
@@ -433,7 +426,7 @@ public class SecureChannel {
                         try {
                             CreateSessionResponse responseMessage;
 
-                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
+                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
                             if (unknownExtensionObject instanceof CreateSessionResponse) {
                                 responseMessage = (CreateSessionResponse) unknownExtensionObject;
 
@@ -538,7 +531,7 @@ public class SecureChannel {
 
             Consumer<byte[]> consumer = opcuaResponse -> {
                 try {
-                    ExtensionObject message = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
+                    ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
                     if (message.getBody() instanceof ServiceFault) {
                         ServiceFault fault = (ServiceFault) message.getBody();
                         LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
@@ -547,7 +540,7 @@ public class SecureChannel {
                         try {
                             ActivateSessionResponse responseMessage;
 
-                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
+                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
                             if (unknownExtensionObject instanceof ActivateSessionResponse) {
                                 responseMessage = (ActivateSessionResponse) unknownExtensionObject;
 
@@ -629,7 +622,7 @@ public class SecureChannel {
 
             Consumer<byte[]> consumer = opcuaResponse -> {
                 try {
-                    ExtensionObject message = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
+                    ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
                     if (message.getBody() instanceof ServiceFault) {
                         ServiceFault fault = (ServiceFault) message.getBody();
                         LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
@@ -638,7 +631,7 @@ public class SecureChannel {
                         try {
                             CloseSessionResponse responseMessage;
 
-                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
+                            ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false).getBody();
                             if (unknownExtensionObject instanceof CloseSessionResponse) {
                                 responseMessage = (CloseSessionResponse) unknownExtensionObject;
 
@@ -794,7 +787,7 @@ public class SecureChannel {
                 .check(p -> p.getRequestId() == transactionId)
                 .handle(opcuaOpenResponse -> {
                     try {
-                        ExtensionObject message = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaOpenResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
+                        ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaOpenResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
                         if (message.getBody() instanceof ServiceFault) {
                             ServiceFault fault = (ServiceFault) message.getBody();
                             LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
@@ -874,7 +867,7 @@ public class SecureChannel {
                 .check(p -> p.getRequestId() == transactionId)
                 .handle(opcuaMessageResponse -> {
                     try {
-                        ExtensionObject message = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaMessageResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
+                        ExtensionObject message = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaMessageResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN), false);
                         if (message.getBody() instanceof ServiceFault) {
                             ServiceFault fault = (ServiceFault) message.getBody();
                             LOGGER.error("Failed to connect to opc ua server for the following reason:- {}, {}", ((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode(), OpcuaStatusCode.enumForValue(((ResponseHeader) fault.getResponseHeader()).getServiceResult().getStatusCode()));
@@ -1021,7 +1014,7 @@ public class SecureChannel {
                         final OpcuaAPU apu;
 
                         if (this.isEncrypted) {
-                            apu = OpcuaAPUIO.staticParse(encryptionHandler.encodeMessage(openRequest, buffer.getData()), false);
+                            apu = OpcuaAPU.staticParse(encryptionHandler.encodeMessage(openRequest, buffer.getData()), false);
                         } else {
                             apu = new OpcuaAPU(openRequest);
                         }
@@ -1035,7 +1028,7 @@ public class SecureChannel {
                             .handle(opcuaOpenResponse -> {
                                 try {
                                     ReadBufferByteBased readBuffer = new ReadBufferByteBased(opcuaOpenResponse.getMessage(), org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
-                                    ExtensionObject message = ExtensionObjectIO.staticParse(readBuffer, false);
+                                    ExtensionObject message = ExtensionObject.staticParse(readBuffer, false);
 
                                     if (message.getBody() instanceof ServiceFault) {
                                         ServiceFault fault = (ServiceFault) message.getBody();
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index 1159f0d..82087bb 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -28,7 +28,6 @@ import org.apache.plc4x.java.opcua.config.OpcuaConfiguration;
 import org.apache.plc4x.java.opcua.context.SecureChannel;
 import org.apache.plc4x.java.opcua.field.OpcuaField;
 import org.apache.plc4x.java.opcua.readwrite.*;
-import org.apache.plc4x.java.opcua.readwrite.io.*;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
@@ -173,7 +172,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             Consumer<byte []> consumer = opcuaResponse -> {
                 PlcReadResponse response = null;
                 try {
-                    ExtensionObjectDefinition reply = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    ExtensionObjectDefinition reply = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                     if (reply instanceof ReadResponse) {
                         future.complete(new DefaultPlcReadResponse(request, readResponse(request.getFieldNames(), ((ReadResponse) reply).getResults())));
                     } else {
@@ -671,7 +670,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             Consumer<byte[]> consumer = opcuaResponse -> {
                 WriteResponse responseMessage = null;
                 try {
-                    responseMessage = (WriteResponse) ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    responseMessage = (WriteResponse) ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                 } catch (ParseException e) {
                     e.printStackTrace();
                 }
@@ -792,7 +791,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             Consumer<byte[]> consumer = opcuaResponse -> {
                 CreateSubscriptionResponse responseMessage = null;
                 try {
-                    responseMessage = (CreateSubscriptionResponse) ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    responseMessage = (CreateSubscriptionResponse) ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                 } catch (ParseException e) {
                     e.printStackTrace();
                 }
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java
index c297508..4159c51 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java
@@ -25,7 +25,6 @@ import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.opcua.context.SecureChannel;
 import org.apache.plc4x.java.opcua.field.OpcuaField;
 import org.apache.plc4x.java.opcua.readwrite.*;
-import org.apache.plc4x.java.opcua.readwrite.io.ExtensionObjectIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent;
@@ -164,7 +163,7 @@ public class OpcuaSubscriptionHandle extends DefaultPlcSubscriptionHandle {
             Consumer<byte[]> consumer = opcuaResponse -> {
                 CreateMonitoredItemsResponse responseMessage = null;
                 try {
-                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                     if (unknownExtensionObject instanceof CreateMonitoredItemsResponse) {
                         responseMessage = (CreateMonitoredItemsResponse) unknownExtensionObject;
                     } else {
@@ -274,7 +273,7 @@ public class OpcuaSubscriptionHandle extends DefaultPlcSubscriptionHandle {
                                 PublishResponse responseMessage = null;
                                 ServiceFault serviceFault = null;
                                 try {
-                                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                                     if (unknownExtensionObject instanceof PublishResponse) {
                                         responseMessage = (PublishResponse) unknownExtensionObject;
                                     } else {
@@ -386,7 +385,7 @@ public class OpcuaSubscriptionHandle extends DefaultPlcSubscriptionHandle {
             Consumer<byte[]> consumer = opcuaResponse -> {
                 DeleteSubscriptionsResponse responseMessage = null;
                 try {
-                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObjectIO.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    ExtensionObjectDefinition unknownExtensionObject = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
                     if (unknownExtensionObject instanceof DeleteSubscriptionsResponse) {
                         responseMessage = (DeleteSubscriptionsResponse) unknownExtensionObject;
                     } else {
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
index daccb72..c387b24 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
@@ -28,7 +28,6 @@ import org.apache.plc4x.java.profinet.field.ProfinetField;
 import org.apache.plc4x.java.profinet.field.ProfinetFieldHandler;
 import org.apache.plc4x.java.profinet.protocol.ProfinetProtocolLogic;
 import org.apache.plc4x.java.profinet.readwrite.Ethernet_Frame;
-import org.apache.plc4x.java.profinet.readwrite.io.Ethernet_FrameIO;
 import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest;
@@ -125,7 +124,7 @@ public class ProfinetDriver extends GeneratedDriverBase<Ethernet_Frame> {
 
     @Override
     protected ProtocolStackConfigurer<Ethernet_Frame> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(Ethernet_Frame.class, Ethernet_FrameIO.class)
+        return SingleProtocolStackConfigurer.builder(Ethernet_Frame.class, Ethernet_Frame::staticParse)
             .withProtocol(ProfinetProtocolLogic.class)
             .withDriverContext(ProfinetDriverContext.class)
             // Every incoming message is to be treated as a response.
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index fdcbc08..6c9626f 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -26,7 +26,6 @@ import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.profinet.ProfinetDriver;
 import org.apache.plc4x.java.profinet.readwrite.*;
-import org.apache.plc4x.java.profinet.readwrite.io.Ethernet_FrameIO;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryItem;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryResponse;
@@ -108,7 +107,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                                     if (isPnPacket) {
                                         ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
                                         try {
-                                            Ethernet_Frame ethernetFrame = Ethernet_FrameIO.staticParse(reader);
+                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
                                             PnDcp_Pdu pdu;
                                             // Access the pdu data (either directly or by
                                             // unpacking the content of the VLAN packet.
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 0f809d8..fe8c2b6 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -26,7 +26,6 @@ import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.profinet.context.ProfinetDriverContext;
 import org.apache.plc4x.java.profinet.readwrite.*;
-import org.apache.plc4x.java.profinet.readwrite.io.DceRpc_PacketIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.generation.*;
@@ -141,7 +140,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             DatagramPacket connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
             udpSocket.receive(connectResponsePacket);
             ReadBufferByteBased readBuffer = new ReadBufferByteBased(resultBuffer);
-            final DceRpc_Packet dceRpc_packet = DceRpc_PacketIO.staticParse(readBuffer);
+            final DceRpc_Packet dceRpc_packet = DceRpc_Packet.staticParse(readBuffer);
             if((dceRpc_packet.getOperation() == DceRpc_Operation.CONNECT) && (dceRpc_packet.getPacketType() == DceRpc_PacketType.RESPONSE)) {
                 if (dceRpc_packet.getPayload().getPacketType() == DceRpc_PacketType.RESPONSE) {
                     // Get the remote MAC address and store it in the context.
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetIoTest.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetIoTest.java
index 234a550..0a0e293 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetIoTest.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetIoTest.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
 import org.apache.plc4x.java.api.messages.PlcReadResponse;
 import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet;
-import org.apache.plc4x.java.profinet.readwrite.io.DceRpc_PacketIO;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
index 9ce3a48..3399de4 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
@@ -22,7 +22,6 @@ import io.netty.buffer.ByteBuf;
 import org.apache.plc4x.java.s7.readwrite.configuration.S7Configuration;
 import org.apache.plc4x.java.s7.readwrite.context.S7DriverContext;
 import org.apache.plc4x.java.s7.readwrite.field.S7Field;
-import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
 import org.apache.plc4x.java.s7.readwrite.optimizer.S7Optimizer;
 import org.apache.plc4x.java.s7.readwrite.protocol.S7ProtocolLogic;
 import org.apache.plc4x.java.s7.readwrite.field.S7PlcFieldHandler;
@@ -103,7 +102,7 @@ public class S7Driver extends GeneratedDriverBase<TPKTPacket> {
 
     @Override
     protected ProtocolStackConfigurer<TPKTPacket> getStackConfigurer() {
-        return SingleProtocolStackConfigurer.builder(TPKTPacket.class, TPKTPacketIO.class)
+        return SingleProtocolStackConfigurer.builder(TPKTPacket.class, TPKTPacket::staticParse)
             .withProtocol(S7ProtocolLogic.class)
             .withDriverContext(S7DriverContext.class)
             .withPacketSizeEstimator(ByteLengthEstimator.class)
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
index 43e05f1..04241d6 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/field/S7Field.java
@@ -28,7 +28,6 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.api.model.PlcField;
 import org.apache.plc4x.java.s7.readwrite.S7Address;
 import org.apache.plc4x.java.s7.readwrite.S7AddressAny;
-import org.apache.plc4x.java.s7.readwrite.io.S7AddressIO;
 import org.apache.plc4x.java.s7.readwrite.MemoryArea;
 import org.apache.plc4x.java.s7.readwrite.TransportSize;
 import org.apache.plc4x.java.spi.generation.*;
@@ -250,7 +249,7 @@ public class S7Field implements PlcField, Serializable {
             try {
                 byte[] addressData = Hex.decodeHex(fieldString.replaceAll("[-]", ""));
                 ReadBuffer rb = new ReadBufferByteBased(addressData);
-                final S7Address s7Address = S7AddressIO.staticParse(rb);
+                final S7Address s7Address = S7Address.staticParse(rb);
                 if (s7Address instanceof S7AddressAny) {
                     S7AddressAny s7AddressAny = (S7AddressAny) s7Address;
 
diff --git a/plc4j/drivers/s7/src/test/java/BenchmarkGeneratedS7.java b/plc4j/drivers/s7/src/test/java/BenchmarkGeneratedS7.java
index 0faafed..42ced4c 100644
--- a/plc4j/drivers/s7/src/test/java/BenchmarkGeneratedS7.java
+++ b/plc4j/drivers/s7/src/test/java/BenchmarkGeneratedS7.java
@@ -19,7 +19,6 @@
 
 import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.s7.readwrite.TPKTPacket;
-import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
@@ -32,13 +31,12 @@ public class BenchmarkGeneratedS7 {
         byte[] rData = Hex.decodeHex("0300001611e00000000f00c2020100c1020311c0010a");
         long start = System.currentTimeMillis();
         int numRunsParse = 2000000;
-        TPKTPacketIO tpktPacketIO = new TPKTPacketIO();
 
         // Benchmark the parsing code
         TPKTPacket packet = null;
         for(int i = 0; i < numRunsParse; i++) {
             ReadBuffer rBuf = new ReadBufferByteBased(rData);
-            packet = TPKTPacketIO.staticParse(rBuf);
+            packet = TPKTPacket.staticParse(rBuf);
         }
         long endParsing = System.currentTimeMillis();
 
diff --git a/plc4j/drivers/s7/src/test/java/S7IoTest.java b/plc4j/drivers/s7/src/test/java/S7IoTest.java
index 06e935a..94728f9 100644
--- a/plc4j/drivers/s7/src/test/java/S7IoTest.java
+++ b/plc4j/drivers/s7/src/test/java/S7IoTest.java
@@ -18,7 +18,6 @@
  */
 
 import org.apache.plc4x.java.s7.readwrite.*;
-import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.java.spi.utils.ascii.AsciiBox;
 import org.junit.jupiter.api.Test;
@@ -381,7 +380,7 @@ public class S7IoTest {
             String gotXml = writeBufferXmlBased.getXmlString();
             assertEquals(wantXml, gotXml);
             ReadBufferXmlBased readBufferXmlBased = new ReadBufferXmlBased(new ByteArrayInputStream(gotXml.getBytes()));
-            TPKTPacket reReadTpktPacket = TPKTPacketIO.staticParse(readBufferXmlBased);
+            TPKTPacket reReadTpktPacket = TPKTPacket.staticParse(readBufferXmlBased);
             assertThat(reReadTpktPacket).usingRecursiveComparison().isEqualTo(tpktPacket);
         }
         // json
@@ -391,7 +390,7 @@ public class S7IoTest {
             String gotJson = writeBufferJsonBased.getJsonString();
             JSONAssert.assertEquals(wantJson, gotJson, JSONCompareMode.LENIENT);
             ReadBufferJsonBased readBufferXmlBased = new ReadBufferJsonBased(new ByteArrayInputStream(gotJson.getBytes()));
-            TPKTPacket reReadTpktPacket = TPKTPacketIO.staticParse(readBufferXmlBased);
+            TPKTPacket reReadTpktPacket = TPKTPacket.staticParse(readBufferXmlBased);
             assertThat(reReadTpktPacket).usingRecursiveComparison().isEqualTo(tpktPacket);
         }
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
index a33226d..c13a98d 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
@@ -31,7 +31,6 @@ import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.context.DriverContext;
 import org.apache.plc4x.java.spi.generation.ByteOrder;
 import org.apache.plc4x.java.spi.generation.Message;
-import org.apache.plc4x.java.spi.generation.MessageIO;
 import org.apache.plc4x.java.spi.generation.MessageInput;
 
 import java.lang.reflect.InvocationTargetException;
@@ -48,22 +47,24 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
     private final ByteOrder byteOrder;
     private final Class<? extends Plc4xProtocolBase<BASE_PACKET_CLASS>> protocolClass;
     private final Class<? extends DriverContext> driverContextClass;
-    private final MessageInput<BASE_PACKET_CLASS> protocolIO;
+    private final MessageInput<BASE_PACKET_CLASS> messageIoClass;
     private final Class<? extends ToIntFunction<ByteBuf>> packetSizeEstimatorClass;
     private final Class<? extends Consumer<ByteBuf>> corruptPacketRemoverClass;
     private final Object[] parserArgs;
 
-    public static <BPC extends Message> SingleProtocolStackBuilder<BPC> builder(Class<BPC> basePacketClass, Class<? extends MessageInput<BPC>> messageIoClass) {
-        return new SingleProtocolStackBuilder<>(basePacketClass, messageIoClass);
+    public static <BPC extends Message> SingleProtocolStackBuilder<BPC> builder(Class<BPC> basePacketClass, MessageInput<BPC> messageIo) {
+        return new SingleProtocolStackBuilder<>(basePacketClass, messageIo);
     }
 
-    /** Only accessible via Builder */
+    /**
+     * Only accessible via Builder
+     */
     SingleProtocolStackConfigurer(Class<BASE_PACKET_CLASS> basePacketClass,
                                   ByteOrder byteOrder,
                                   Object[] parserArgs,
                                   Class<? extends Plc4xProtocolBase<BASE_PACKET_CLASS>> protocol,
                                   Class<? extends DriverContext> driverContextClass,
-                                  MessageInput<BASE_PACKET_CLASS> protocolIO,
+                                  MessageInput<BASE_PACKET_CLASS> messageIoClass,
                                   Class<? extends ToIntFunction<ByteBuf>> packetSizeEstimatorClass,
                                   Class<? extends Consumer<ByteBuf>> corruptPacketRemoverClass) {
         this.basePacketClass = basePacketClass;
@@ -71,24 +72,26 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
         this.parserArgs = parserArgs;
         this.protocolClass = protocol;
         this.driverContextClass = driverContextClass;
-        this.protocolIO = protocolIO;
+        this.messageIoClass = messageIoClass;
         this.packetSizeEstimatorClass = packetSizeEstimatorClass;
         this.corruptPacketRemoverClass = corruptPacketRemoverClass;
     }
 
     private ChannelHandler getMessageCodec(Configuration configuration) {
-        return new GeneratedProtocolMessageCodec<>(basePacketClass, protocolIO, byteOrder, parserArgs,
+        return new GeneratedProtocolMessageCodec<>(basePacketClass, messageIoClass, byteOrder, parserArgs,
             packetSizeEstimatorClass != null ? configure(configuration, createInstance(packetSizeEstimatorClass)) : null,
             corruptPacketRemoverClass != null ? configure(configuration, createInstance(corruptPacketRemoverClass)) : null);
     }
 
-    /** Applies the given Stack to the Pipeline */
+    /**
+     * Applies the given Stack to the Pipeline
+     */
     @Override
     public Plc4xProtocolBase<BASE_PACKET_CLASS> configurePipeline(
         Configuration configuration, ChannelPipeline pipeline, boolean passive, List<EventListener> ignore) {
         pipeline.addLast(getMessageCodec(configuration));
         Plc4xProtocolBase<BASE_PACKET_CLASS> protocol = configure(configuration, createInstance(protocolClass));
-        if(driverContextClass != null) {
+        if (driverContextClass != null) {
             protocol.setDriverContext(configure(configuration, createInstance(driverContextClass)));
         }
         Plc4xNettyWrapper<BASE_PACKET_CLASS> context = new Plc4xNettyWrapper<>(pipeline, passive, protocol, basePacketClass);
@@ -99,11 +102,11 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
     private <T> T createInstance(Class<T> clazz, Object... args) {
         try {
             Class<?>[] parameterTypes = new Class<?>[args.length];
-            for(int i = 0; i < args.length; i++) {
+            for (int i = 0; i < args.length; i++) {
                 parameterTypes[i] = args[i].getClass();
             }
             return clazz.getDeclaredConstructor(parameterTypes).newInstance(args);
-        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException  e) {
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
             throw new PlcRuntimeException("Error creating instance of class " + clazz.getName());
         }
     }
@@ -116,7 +119,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
     public static final class SingleProtocolStackBuilder<BASE_PACKET_CLASS extends Message> {
 
         private final Class<BASE_PACKET_CLASS> basePacketClass;
-        private final Class<? extends MessageInput<BASE_PACKET_CLASS>> messageIoClass;
+        private final MessageInput<BASE_PACKET_CLASS> messageIo;
         private Class<? extends DriverContext> driverContextClass;
         private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
         private Object[] parserArgs;
@@ -124,9 +127,9 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
         private Class<? extends ToIntFunction<ByteBuf>> packetSizeEstimator;
         private Class<? extends Consumer<ByteBuf>> corruptPacketRemover;
 
-        public SingleProtocolStackBuilder(Class<BASE_PACKET_CLASS> basePacketClass, Class<? extends MessageInput<BASE_PACKET_CLASS>> messageIoClass) {
+        public SingleProtocolStackBuilder(Class<BASE_PACKET_CLASS> basePacketClass, MessageInput<BASE_PACKET_CLASS> messageIo) {
             this.basePacketClass = basePacketClass;
-            this.messageIoClass = messageIoClass;
+            this.messageIo = messageIo;
         }
 
         public SingleProtocolStackBuilder<BASE_PACKET_CLASS> withDriverContext(Class<? extends DriverContext> driverContextClass) {
@@ -171,13 +174,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
 
         public SingleProtocolStackConfigurer<BASE_PACKET_CLASS> build() {
             assert this.protocol != null;
-            try {
-                final MessageInput messageIo = messageIoClass.getDeclaredConstructor().newInstance();
-                return new SingleProtocolStackConfigurer<>(
-                    basePacketClass, byteOrder, parserArgs, protocol, driverContextClass, messageIo, packetSizeEstimator, corruptPacketRemover);
-            } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
-                throw new PlcRuntimeException("Error initializing MessageIO instance", e);
-            }
+            return new SingleProtocolStackConfigurer<>(basePacketClass, byteOrder, parserArgs, protocol, driverContextClass, messageIo, packetSizeEstimator, corruptPacketRemover);
         }
 
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/MessageInput.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/MessageInput.java
index 6fa7465..b9f348d 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/MessageInput.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/MessageInput.java
@@ -18,6 +18,7 @@
  */
 package org.apache.plc4x.java.spi.generation;
 
+@FunctionalInterface
 public interface MessageInput<PARSER_TYPE> {
 
     PARSER_TYPE parse(ReadBuffer io, Object... args) throws ParseException;
diff --git a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java
index aefd808..b422169 100644
--- a/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java
+++ b/plc4j/transports/socketcan/src/main/java/org/apache/plc4x/java/transport/socketcan/SocketCANTransport.java
@@ -25,7 +25,6 @@ import java.util.function.ToIntFunction;
 
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.socketcan.readwrite.SocketCANFrame;
-import org.apache.plc4x.java.socketcan.readwrite.io.SocketCANFrameIO;
 import org.apache.plc4x.java.spi.configuration.Configuration;
 import org.apache.plc4x.java.spi.connection.ChannelFactory;
 import org.apache.plc4x.java.spi.generation.*;
@@ -62,7 +61,7 @@ public class SocketCANTransport implements CANTransport<SocketCANFrame> {
 
     @Override
     public MessageInput<SocketCANFrame> getMessageInput(Configuration cfg) {
-        return new SocketCANFrameIO();
+        return SocketCANFrame::staticParse;
     }
 
     @Override
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageResolver.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageResolver.java
index ebd5206..78e4d20 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageResolver.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageResolver.java
@@ -19,6 +19,7 @@
 package org.apache.plc4x.test.migration;
 
 import java.util.Map;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.test.driver.exceptions.DriverTestsuiteException;
@@ -34,8 +35,6 @@ import java.util.List;
 
 public class MessageResolver {
 
-    private final static Logger LOGGER = LoggerFactory.getLogger(MessageResolver.class);
-
     /**
      * Returns the messageIO class based on a configured package. convention out of {@code protocolName} {@code outputFlavor} and {@code name}.
      * If this fails its tries a fallback using the deprecated attribute {@code className}
@@ -46,110 +45,71 @@ public class MessageResolver {
      * @throws DriverTestsuiteException if a MessageIO couldn't be found.
      */
     @SuppressWarnings("rawtypes")
-    public static MessageInput getMessageInput(Map<String, String> options, String name) throws DriverTestsuiteException {
+    public static MessageInput<?> getMessageInput(Map<String, String> options, String name) throws DriverTestsuiteException {
         try {
-            return MessageResolver.getMessageIOType(options, name).getMessageInput().newInstance();
-        } catch (InstantiationException | IllegalAccessException e) {
+            return MessageResolver.getMessageIOType(options, name).getMessageInput();
+        } catch (ClassNotFoundException e) {
             throw new DriverTestsuiteException(e);
         }
     }
 
-    public static MessageInput<Message> getMessageIOStaticLinked(Map<String, String> options, String typeName) throws ParserSerializerTestsuiteException {
+    public static MessageInput<?> getMessageIOStaticLinked(Map<String, String> options, String typeName) throws ParserSerializerTestsuiteException {
         try {
-            TypePair typePair = getMessageIOType(options, typeName);
-            Class<?> ioRootClass = typePair.getType();
-            Class<?> ioClass = typePair.getMessageInput();
-            Method staticParseMethod = null;
-            Method staticSerializeMethod = null;
+            TypeMessageInput typeMessageInput = getMessageIOType(options, typeName);
             final List<Class<?>> parameterTypes = new LinkedList<>();
-            for (Method method : ioClass.getMethods()) {
-                if (method.getName().equals("staticParse") && Modifier.isStatic(method.getModifiers()) &&
-                    (method.getReturnType() == ioRootClass)) {
-                    staticParseMethod = method;
-
+            for (Method method : typeMessageInput.type.getMethods()) {
+                int parameterCount = method.getParameterCount();
+                boolean isNonGenericParse = parameterCount > 1 && method.getParameterTypes()[parameterCount - 1] != Object[].class;
+                if (method.getName().equals("staticParse") && Modifier.isStatic(method.getModifiers()) && isNonGenericParse) {
                     // Get a list of additional parameter types for the parser.
-                    for (int i = 1; i < method.getParameterCount(); i++) {
-                        Class<?> parameterType = staticParseMethod.getParameterTypes()[i];
+                    for (int i = 1; i < parameterCount; i++) {
+                        Class<?> parameterType = method.getParameterTypes()[i];
                         parameterTypes.add(parameterType);
                     }
+                    break;
                 }
-                if (method.getName().equals("staticSerialize") && Modifier.isStatic(method.getModifiers()) &&
-                    (method.getParameterTypes()[1] == ioRootClass)) {
-                    staticSerializeMethod = method;
-                }
-            }
-            if (staticParseMethod == null) {
-                throw new ParserSerializerTestsuiteException(
-                    "Unable to instantiate IO component. Missing static parse or serialize methods.");
             }
-            final Method parseMethod = staticParseMethod;
-            final Method serializeMethod = staticSerializeMethod;
-            return new MessageIO() {
-                @Override
-                public Object parse(ReadBuffer io, Object... args) throws ParseException {
-                    try {
-                        Object[] argValues = new Object[args.length + 1];
-                        argValues[0] = io;
-                        for (int i = 1; i <= args.length; i++) {
-                            String parameterValue = (String) args[i - 1];
-                            Class<?> parameterType = parameterTypes.get(i - 1);
-                            if (parameterType == Boolean.class) {
-                                argValues[i] = Boolean.parseBoolean(parameterValue);
-                            } else if (parameterType == Byte.class) {
-                                argValues[i] = Byte.parseByte(parameterValue);
-                            } else if (parameterType == Short.class) {
-                                argValues[i] = Short.parseShort(parameterValue);
-                            } else if (parameterType == Integer.class) {
-                                argValues[i] = Integer.parseInt(parameterValue);
-                            } else if (parameterType == Long.class) {
-                                argValues[i] = Long.parseLong(parameterValue);
-                            } else if (parameterType == Float.class) {
-                                argValues[i] = Float.parseFloat(parameterValue);
-                            } else if (parameterType == Double.class) {
-                                argValues[i] = Double.parseDouble(parameterValue);
-                            } else if (parameterType == String.class) {
-                                argValues[i] = parameterValue;
-                            } else if (Enum.class.isAssignableFrom(parameterType)) {
-                                argValues[i] = Enum.valueOf((Class<? extends Enum>) parameterType, parameterValue);
-                            } else {
-                                throw new ParseException("Currently unsupported parameter type");
-                            }
-                        }
-
-                        return parseMethod.invoke(null, argValues);
-                    } catch (IllegalAccessException | InvocationTargetException e) {
-                        throw new ParseException("error parsing", e);
-                    }
-                }
-
-                @Override
-                public void serialize(WriteBuffer io, Object value, Object... args) throws SerializationException {
-                    try {
-                        if(serializeMethod != null) {
-                            serializeMethod.invoke(null, io, value);
-                        } else if(value instanceof Message) {
-                            Message message = (Message) value;
-                            message.serialize(io);
-                        } else {
-                            throw new SerializationException("Error finding a serializer for this message");
-                        }
-                    } catch (IllegalAccessException | InvocationTargetException | SerializationException e) {
-                        throw new SerializationException("Error serializing", e);
+            return (io, args) -> {
+                Object[] argValues = new Object[args.length];
+                for (int i = 0; i < args.length; i++) {
+                    String parameterValue = (String) args[i];
+                    Class<?> parameterType = parameterTypes.get(i);
+                    if (parameterType == Boolean.class) {
+                        argValues[i] = Boolean.parseBoolean(parameterValue);
+                    } else if (parameterType == Byte.class) {
+                        argValues[i] = Byte.parseByte(parameterValue);
+                    } else if (parameterType == Short.class) {
+                        argValues[i] = Short.parseShort(parameterValue);
+                    } else if (parameterType == Integer.class) {
+                        argValues[i] = Integer.parseInt(parameterValue);
+                    } else if (parameterType == Long.class) {
+                        argValues[i] = Long.parseLong(parameterValue);
+                    } else if (parameterType == Float.class) {
+                        argValues[i] = Float.parseFloat(parameterValue);
+                    } else if (parameterType == Double.class) {
+                        argValues[i] = Double.parseDouble(parameterValue);
+                    } else if (parameterType == String.class) {
+                        argValues[i] = parameterValue;
+                    } else if (Enum.class.isAssignableFrom(parameterType)) {
+                        argValues[i] = Enum.valueOf((Class<? extends Enum>) parameterType, parameterValue);
+                    } else {
+                        throw new ParseException("Currently unsupported parameter type");
                     }
                 }
+                return typeMessageInput.getMessageInput().parse(io, argValues);
             };
-        } catch (DriverTestsuiteException e) {
+        } catch (DriverTestsuiteException | ClassNotFoundException e) {
             throw new ParserSerializerTestsuiteException("Unable to instantiate IO component", e);
         }
     }
 
     @SuppressWarnings("unchecked")
-    private static TypePair getMessageIOType(Map<String, String> options, String typeName) throws DriverTestsuiteException {
+    private static TypeMessageInput getMessageIOType(Map<String, String> options, String typeName) throws DriverTestsuiteException, ClassNotFoundException {
         String extraMessage = "";
         if (options.containsKey("package")) {
             try {
                 return lookup(options.get("package"), typeName);
-            } catch (ClassNotFoundException e) {
+            } catch (NoSuchMethodException e) {
                 extraMessage = "custom package '" + options.get("package") + "' and ";
             }
         }
@@ -159,38 +119,47 @@ public class MessageResolver {
         String classPackage = String.format("org.apache.plc4x.java.%s.%s", protocolName, StringUtils.replace(outputFlavor, "-", ""));
         try {
             return lookup(classPackage, typeName);
-        } catch (ClassNotFoundException e) {
+        } catch (NoSuchMethodException e) {
             throw new DriverTestsuiteException("Could not find " + typeName + " in " + extraMessage + "standard package '" + classPackage + "'");
         }
     }
 
-    private static TypePair lookup(String driverPackage, String typeName) throws ClassNotFoundException {
+    private static TypeMessageInput lookup(String driverPackage, String typeName) throws ClassNotFoundException, NoSuchMethodException {
         try {
             Package.getPackage(driverPackage);
         } catch (RuntimeException e) {
             throw new DriverTestsuiteException("Invalid or non existent package detected: " + driverPackage, e);
         }
         String ioRootClassName = driverPackage + "." + typeName;
-        String ioClassName = driverPackage + ".io." + typeName + "IO";
         // make sure both type and it's IO are present
-        return new TypePair(
-            (Class<? extends Message>) Class.forName(ioRootClassName),
-            (Class<? extends MessageInput<?>>) Class.forName(ioClassName)
+        Class<? extends Message> messageType = (Class<? extends Message>) Class.forName(ioRootClassName);
+        Method staticParse = messageType.getMethod("staticParse", ReadBuffer.class, Object[].class);
+        return new TypeMessageInput(
+            messageType,
+            (io, args) -> {
+                try {
+                    return staticParse.invoke(null, io, args);
+                } catch (IllegalAccessException | InvocationTargetException e) {
+                    throw new RuntimeException(e);
+                }
+            }
         );
     }
 
-    static class TypePair {
+    static class TypeMessageInput {
         private final Class<? extends Message> type;
-        private final Class<? extends MessageInput<?>> messageInput;
+        private final MessageInput<?> messageInput;
 
-        TypePair(Class<? extends Message> type, Class<? extends MessageInput<?>> messageInput) {
+        TypeMessageInput(Class<? extends Message> type, MessageInput<?> messageInput) {
             this.type = type;
             this.messageInput = messageInput;
         }
+
         Class<?> getType() {
             return type;
         }
-        Class<? extends MessageInput<?>> getMessageInput() {
+
+        MessageInput<?> getMessageInput() {
             return messageInput;
         }
     }
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageValidatorAndMigrator.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageValidatorAndMigrator.java
index c822aba..fd700c4 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageValidatorAndMigrator.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/migration/MessageValidatorAndMigrator.java
@@ -64,7 +64,7 @@ public class MessageValidatorAndMigrator {
      */
     @SuppressWarnings({"rawtypes"})
     public static void validateOutboundMessageAndMigrate(String testCaseName, Map<String, String> options, Element referenceXml, List<String> parserArguments, byte[] data, ByteOrder byteOrder, boolean autoMigrate, URI siteURI) throws DriverTestsuiteException {
-        MessageInput<Message> messageInput = MessageResolver.getMessageInput(options, referenceXml.getName());
+        MessageInput<?> messageInput = MessageResolver.getMessageInput(options, referenceXml.getName());
         validateOutboundMessageAndMigrate(testCaseName, messageInput, referenceXml, parserArguments, data, byteOrder, autoMigrate, siteURI);
     }
 
@@ -83,11 +83,11 @@ public class MessageValidatorAndMigrator {
      * @throws DriverTestsuiteException if something goes wrong
      */
     @SuppressWarnings({"rawtypes", "unchecked"})
-    public static boolean validateOutboundMessageAndMigrate(String testCaseName, MessageInput<Message> messageInput, Element referenceXml, List<String> parserArguments, byte[] data, ByteOrder byteOrder, boolean autoMigrate, URI siteURI) throws DriverTestsuiteException {
+    public static boolean validateOutboundMessageAndMigrate(String testCaseName, MessageInput<?> messageInput, Element referenceXml, List<String> parserArguments, byte[] data, ByteOrder byteOrder, boolean autoMigrate, URI siteURI) throws DriverTestsuiteException {
         final ReadBufferByteBased readBuffer = new ReadBufferByteBased(data, byteOrder);
 
         try {
-            final Message parsedOutput = messageInput.parse(readBuffer, parserArguments.toArray());
+            final Message parsedOutput = (Message) messageInput.parse(readBuffer, parserArguments.toArray());
             final String referenceXmlString = referenceXml.asXML();
             try {
                 // First try to use the native xml writer
@@ -186,7 +186,7 @@ public class MessageValidatorAndMigrator {
      */
     @SuppressWarnings("rawtypes")
     public static Message validateInboundMessageAndGet(Map<String, String> options, Element referenceXml, List<String> parserArguments) {
-        MessageInput<Message> messageIO = MessageResolver.getMessageInput(options, referenceXml.getName());
+        MessageInput<?> messageIO = MessageResolver.getMessageInput(options, referenceXml.getName());
         return validateInboundMessageAndGet(messageIO, referenceXml, parserArguments);
     }
 
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/parserserializer/ParserSerializerTestsuiteRunner.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/parserserializer/ParserSerializerTestsuiteRunner.java
index 3f205f8..ad2994c 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/parserserializer/ParserSerializerTestsuiteRunner.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/parserserializer/ParserSerializerTestsuiteRunner.java
@@ -146,12 +146,12 @@ public class ParserSerializerTestsuiteRunner extends XmlTestsuiteLoader {
         ReadBufferByteBased readBuffer = new ReadBufferByteBased(testcaseRaw, testSuite.getByteOrder());
 
         try {
-            MessageInput<Message> messageInput = MessageResolver.getMessageIOStaticLinked(
+            MessageInput<?> messageInput = MessageResolver.getMessageIOStaticLinked(
                 testSuite.getOptions(),
                 testcase.getXml().elements().get(0).getName()
             );
             LOGGER.trace("Parsing message");
-            Message parsedOutput = messageInput.parse(readBuffer, testcase.getParserArguments().toArray());
+            Message parsedOutput = (Message) messageInput.parse(readBuffer, testcase.getParserArguments().toArray());
             LOGGER.trace("Validating and migrating");
             boolean migrated = MessageValidatorAndMigrator.validateOutboundMessageAndMigrate(
                 testcase.getName(),