MTI TEK
  • Home
  • About
  • LLMs
  • Docker
  • Kubernetes
  • Java
  • All Resources
Code Samples | JAXB Marshalling and Unmarshalling with Schema Validation
  1. Introduction and Overview
  2. Project Structure and Layout
  3. Maven Dependencies and Build Configuration
  4. Resource Loading Utilities
  5. JAXB Context Helper Methods
  6. XML Schema Validation Utilities
  7. JAXB Marshalling Utilities
  8. JAXB Unmarshalling Utilities
  9. Payload Model Class with JAXB Annotations
  10. Parameter Model Class with JAXB Annotations
  11. Package-level Namespace Configuration
  12. XML Schema Definition (XSD)
  13. Sample XML Data File
  14. Logging Configuration
  15. Java Objects to XML Marshalling Demo
  16. XML to Java Objects Unmarshalling Demo

  1. Introduction and Overview
    This tutorial demonstrates how to use the JAXB (Java Architecture for XML Binding) API to perform two fundamental XML processing operations in Java:
    • Marshal Java objects into XML data: Java Objects to XML Marshalling Demo
      Convert Java objects into their XML representation with proper formatting, namespace handling, and schema compliance.
    • Unmarshal XML data into Java objects: XML to Java Objects Unmarshalling Demo
      Parse XML documents and automatically populate Java objects with validation against XSD schemas.
    Note: The following files are provided as examples to demonstrate the usage of JAXB APIs:
    JAXB Model Classes:
    • Payload.java - Main payload class with JAXB annotations.
    • PayloadParameter.java - Parameter class with JAXB annotations.
    • package-info.java - Package-level namespace configuration.
    Supporting Files:
    • payload.xsd - XML Schema definition.
    • payload.xml - Sample XML data file.
    • payload.json - Sample JSON data file.
    These sample files illustrate practical implementation patterns and can be adapted for your specific use cases.
  2. Project Structure and Layout
    Maven project layout showing organized packages for marshalling, payload models, utilities, and resources.
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── mtitek
            │       └── jaxb
            │           ├── marshalling
            │           │   ├── TestMarshaller.java
            │           │   ├── TestUnmarshaller.java
            │           │   └── payload
            │           │        ├── Payload.java
            │           │        ├── PayloadParameter.java
            │           │        └── package-info.java
            │           └── utils
            │               ├── JAXBContextUtils.java
            │               ├── MarshallerUtils.java
            │               ├── ResourceUtils.java
            │               ├── SchemaUtils.java
            │               └── UnmarshallerUtils.java
            └── resources
                ├── logback.xml
                ├── payload.xml
                └── payload.xsd
    
  3. Maven Dependencies and Build Configuration
    Maven setup with Java 23, Jakarta XML Binding API, Glassfish JAXB runtime, and Apache Commons dependencies.
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>mtitek.jaxb.marshalling</groupId>
        <artifactId>mtitek-jaxb-marshalling</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    
        <name>mtitek-jaxb-marshalling</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    
            <java.version>23</java.version>
    
            <maven.compiler.source>23</maven.compiler.source>
            <maven.compiler.target>23</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.18.0</version>
            </dependency>
    
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.19.0</version>
            </dependency>
    
            <dependency>
                <groupId>jakarta.xml.bind</groupId>
                <artifactId>jakarta.xml.bind-api</artifactId>
                <version>4.0.2</version>
            </dependency>
    
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-runtime</artifactId>
                <version>4.0.5</version>
            </dependency>
    
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.4.14</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.14.0</version>
    
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>3.8.1</version>
    
                    <executions>
                        <execution>
                            <id>dependency-analyze</id>
                            <goals>
                                <goal>analyze</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
  4. Resource Loading Utilities
    Utility class for loading resources from classpath as streams or strings using Apache Commons IO.
    package mtitek.jaxb.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.commons.io.IOUtils;
    
    public class ResourceUtils {
        private ResourceUtils() {
        }
    
        public static InputStream getResourceAsStream(final String fileName) {
            return Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        }
    
        public static String getResourceAsString(final String fileName) throws IOException {
            try (final InputStream inputStream = ResourceUtils.getResourceAsStream(fileName)) {
                return IOUtils.toString(inputStream, "UTF-8");
            }
        }
    }
    
  5. JAXB Context Helper Methods
    Helper class to create JAXB contexts with support for multiple classes using varargs and Optional return types.
    package mtitek.jaxb.utils;
    
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBException;
    
    import org.apache.commons.lang3.ArrayUtils;
    
    public class JAXBContextUtils {
        private JAXBContextUtils() {
        }
    
        public static <T> Optional<JAXBContext> getJAXBContext(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            return Optional.ofNullable(JAXBContext.newInstance(ArrayUtils.add(classesToBeBound, mainClassToBeBound)));
        }
    }
    
  6. XML Schema Validation Utilities
    Utility for creating XML Schema objects from XSD files with proper resource management and stream handling.
    package mtitek.jaxb.utils;
    
    import java.io.IOException;
    import java.util.Optional;
    import java.util.stream.Stream;
    
    import javax.xml.XMLConstants;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.validation.Schema;
    import javax.xml.validation.SchemaFactory;
    
    import org.apache.commons.lang3.ArrayUtils;
    import org.xml.sax.SAXException;
    
    public class SchemaUtils {
        private SchemaUtils() {
        }
    
        public static Optional<Schema> getSchema(final String... filesNames) throws SAXException, IOException {
            Schema schema = null;
            StreamSource[] streamSources = null;
    
            if (!ArrayUtils.isEmpty(filesNames)) {
                try {
                    streamSources = Stream.of(filesNames).map(fileName -> ResourceUtils.getResourceAsStream(fileName))
                            .filter(inputStream -> inputStream != null).map(inputStream -> new StreamSource(inputStream))
                            .toArray(StreamSource[]::new);
    
                    if (!ArrayUtils.isEmpty(streamSources)) {
                        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    
                        schema = schemaFactory.newSchema(streamSources);
                    }
                } finally {
                    if (!ArrayUtils.isEmpty(streamSources)) {
                        Stream.of(streamSources).forEach(streamSource -> {
                            try {
                                if (streamSource.getInputStream() != null) {
                                    streamSource.getInputStream().close();
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
                    }
                }
            }
    
            return Optional.ofNullable(schema);
        }
    }
    
  7. JAXB Marshalling Utilities
    Marshalling utility that converts Java objects to XML with optional QName support and formatted output.
    package mtitek.jaxb.utils;
    
    import java.io.OutputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBElement;
    import jakarta.xml.bind.JAXBException;
    import jakarta.xml.bind.Marshaller;
    import javax.xml.namespace.QName;
    
    public class MarshallerUtils {
        private MarshallerUtils() {
        }
    
        public static <T> void marshal(final OutputStream outputStream, final T object, final QName qName,
                final Class<T> mainClassToBeBound, final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<Marshaller> optionalMarshaller = MarshallerUtils.createMarshaller(mainClassToBeBound,
                    classesToBeBound);
    
            if (optionalMarshaller.isPresent()) {
                Marshaller marshaller = optionalMarshaller.get();
    
                if (qName == null) {
                    marshaller.marshal(object, outputStream);
                } else {
                    final JAXBElement<T> jaxbElement = new JAXBElement<>(qName, mainClassToBeBound, object);
                    marshaller.marshal(jaxbElement, outputStream);
                }
            } else {
                throw new JAXBException("Failed to create the marshaller!");
            }
        }
    
        private static <T> Optional<Marshaller> createMarshaller(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<JAXBContext> jaxbContext = JAXBContextUtils.getJAXBContext(mainClassToBeBound, classesToBeBound);
    
            Marshaller marshaller = null;
    
            if (jaxbContext.isPresent()) {
                marshaller = jaxbContext.get().createMarshaller();
    
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            }
    
            return Optional.ofNullable(marshaller);
        }
    }
    
  8. JAXB Unmarshalling Utilities
    Unmarshalling utility that converts XML to Java objects with schema validation and error handling.
    package mtitek.jaxb.utils;
    
    import java.io.InputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBContext;
    import jakarta.xml.bind.JAXBElement;
    import jakarta.xml.bind.JAXBException;
    import jakarta.xml.bind.Unmarshaller;
    import jakarta.xml.bind.helpers.DefaultValidationEventHandler;
    import javax.xml.transform.stream.StreamSource;
    import javax.xml.validation.Schema;
    
    public class UnmarshallerUtils {
        private UnmarshallerUtils() {
        }
    
        public static <T> Optional<T> unmarshal(final InputStream inputStream, final Schema schema,
                final Class<T> mainClassToBeBound, final Class<?>... classesToBeBound) throws JAXBException {
            if (inputStream == null) {
                throw new IllegalArgumentException("InputStream cannot be null");
            }
    
            final Optional<Unmarshaller> optionalUnmarshaller = UnmarshallerUtils.createUnmarshaller(mainClassToBeBound,
                    classesToBeBound);
    
            T instance = null;
    
            if (optionalUnmarshaller.isPresent()) {
                Unmarshaller unmarshaller = optionalUnmarshaller.get();
    
                if (schema != null) {
                    unmarshaller.setSchema(schema);
                    unmarshaller.setEventHandler(new DefaultValidationEventHandler());
                }
    
                final StreamSource streamSource = new StreamSource(inputStream);
    
                final JAXBElement<T> jaxbElement = unmarshaller.unmarshal(streamSource, mainClassToBeBound);
    
                if (jaxbElement != null) {
                    instance = jaxbElement.getValue();
                }
            } else {
                throw new JAXBException("Failed to create the unmarshaller!");
            }
    
            return Optional.ofNullable(instance);
        }
    
        private static <T> Optional<Unmarshaller> createUnmarshaller(final Class<T> mainClassToBeBound,
                final Class<?>... classesToBeBound) throws JAXBException {
            final Optional<JAXBContext> jaxbContext = JAXBContextUtils.getJAXBContext(mainClassToBeBound, classesToBeBound);
    
            Unmarshaller unmarshaller = null;
    
            if (jaxbContext.isPresent()) {
                unmarshaller = jaxbContext.get().createUnmarshaller();
            }
    
            return Optional.ofNullable(unmarshaller);
        }
    }
    
  9. Payload Model Class with JAXB Annotations
    Main JAXB-annotated model class representing a payload with ID, code, and collection of parameters.
    package mtitek.jaxb.marshalling.payload;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import jakarta.xml.bind.annotation.XmlAccessType;
    import jakarta.xml.bind.annotation.XmlAccessorType;
    import jakarta.xml.bind.annotation.XmlElement;
    import jakarta.xml.bind.annotation.XmlElementWrapper;
    import jakarta.xml.bind.annotation.XmlRootElement;
    import jakarta.xml.bind.annotation.XmlTransient;
    import jakarta.xml.bind.annotation.XmlType;
    
    @XmlRootElement(name = "payload")
    @XmlType(name = "payload", propOrder = { "id", "code", "parameters" })
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Payload {
        @XmlElement(name = "id", required = true)
        private String id;
    
        @XmlElement(name = "code", required = true)
        private String code;
    
        @XmlTransient
        private String desc;
    
        @XmlElementWrapper(name = "parameters")
        @XmlElement(name = "parameter")
        private final List<PayloadParameter> parameters = new ArrayList<>();
    
        public String getId() {
            return id;
        }
    
        public void setId(String value) {
            this.id = value;
        }
    
        public String getCode() {
            return code;
        }
    
        public void setCode(String value) {
            this.code = value;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        public List<PayloadParameter> getParameters() {
            List<PayloadParameter> copyParameters = new ArrayList<>();
            copyParameters.addAll(this.parameters);
            return copyParameters;
        }
    
        public void setParameters(final List<PayloadParameter> parameters) {
            this.parameters.clear();
    
            if (parameters != null) {
                this.parameters.addAll(parameters);
            }
        }
    
        @Override
        public String toString() {
            return "Payload [id=" + id + ", code=" + code + "]";
        }
    }
    
  10. Parameter Model Class with JAXB Annotations
    JAXB model for individual parameters with ID attribute and name/value elements.
    package mtitek.jaxb.marshalling.payload;
    
    import jakarta.xml.bind.annotation.XmlAccessType;
    import jakarta.xml.bind.annotation.XmlAccessorType;
    import jakarta.xml.bind.annotation.XmlAttribute;
    import jakarta.xml.bind.annotation.XmlElement;
    import jakarta.xml.bind.annotation.XmlRootElement;
    import jakarta.xml.bind.annotation.XmlType;
    
    @XmlRootElement(name = "parameter")
    @XmlType(name = "parameterType", propOrder = { "name", "value" })
    @XmlAccessorType(XmlAccessType.FIELD)
    public class PayloadParameter {
    
        @XmlAttribute(name = "id", required = true)
        private String id;
    
        @XmlElement(name = "name", required = true)
        private String name;
    
        @XmlElement(name = "value", required = true)
        private String value;
    
        public String getId() {
            return id;
        }
    
        public void setId(String value) {
            this.id = value;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String value) {
            this.name = value;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "PayloadParameter [id=" + id + ", name=" + name + ", value=" + value + "]";
        }
    }
    
  11. Package-level Namespace Configuration
    Package-level JAXB configuration defining XML namespace and element form defaults.
    @jakarta.xml.bind.annotation.XmlSchema(namespace = "http://marshalling.jaxb.mtitek/payload", elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED)
    package mtitek.jaxb.marshalling.payload;
    
  12. XML Schema Definition (XSD)
    XML Schema definition specifying the structure and validation rules for payload XML documents.
    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        targetNamespace="http://marshalling.jaxb.mtitek/payload"
        xmlns="http://marshalling.jaxb.mtitek/payload"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.1">
    
        <xs:element name="payload">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="id" type="xs:string" />
    
                    <xs:element name="code" type="xs:string" />
    
                    <xs:element name="parameters" type="parametersType"
                        minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:complexType name="parametersType">
            <xs:sequence>
                <xs:element name="parameter" type="parameterType"
                    minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    
        <xs:complexType name="parameterType">
            <xs:all>
                <xs:element name="name" type="xs:string" />
                <xs:element name="value" type="xs:string" />
            </xs:all>
    
            <xs:attribute name="id" type="xs:string" />
        </xs:complexType>
    </xs:schema>
    
  13. Sample XML Data File
    Example XML document demonstrating the expected format with namespace declarations and schema location.
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <payload xmlns="http://marshalling.jaxb.mtitek/payload"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://marshalling.jaxb.mtitek/payload payload.xsd">
        <id>123</id>
        <code>xyz</code>
    
        <parameters>
            <parameter id="11">
                <name>bar</name>
                <value>foo</value>
            </parameter>
    
            <parameter id="22">
                <value>ti</value>
                <name>ta</name>
            </parameter>
        </parameters>
    </payload>
    
  14. Logging Configuration
    Logging configuration for console output with structured formatting and appropriate log levels.
    <configuration>
        <appender name="stdout"
            class="ch.qos.logback.core.ConsoleAppender">
            <target>System.out</target>
    
            <encoder>
                <pattern>%p [%d{ISO8601}] %c - %m%n
                </pattern>
            </encoder>
        </appender>
    
        <logger name="mtitek.jaxb.marshalling" level="INFO" />
    
        <root level="info">
            <appender-ref ref="stdout" />
        </root>
    </configuration>
    
  15. Java Objects to XML Marshalling Demo
    Demonstration class showing how to create Java objects and marshal them to formatted XML output.
    package mtitek.jaxb.marshalling;
    
    import java.io.ByteArrayOutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    import mtitek.jaxb.marshalling.payload.Payload;
    import mtitek.jaxb.marshalling.payload.PayloadParameter;
    import mtitek.jaxb.utils.MarshallerUtils;
    
    /**
     * Test class demonstrating JAXB marshalling from Java objects to XML.
     */
    public class TestMarshaller {
    
        public static void main(String[] args) throws Exception {
            final Payload payload = initPayload();
    
            try (final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                // Marshal payload to XML - null QName uses default namespace
                MarshallerUtils.marshal(byteArrayOutputStream, payload, null, Payload.class);
                System.out.println(byteArrayOutputStream.toString("UTF-8"));
            }
        }
    
        /**
         * Initialize test payload with sample data.
         */
        private static Payload initPayload() {
            final Payload payload = new Payload();
            payload.setId("1");
            payload.setCode("111");
    
            // @XmlTransient field - won't appear in XML output
            payload.setDesc("transient");
    
            final List<PayloadParameter> parameters = new ArrayList<>();
    
            // Add first parameter
            {
                final PayloadParameter parameter = new PayloadParameter();
                parameter.setId("1");
                parameter.setName("aaa");
                parameter.setValue("bbb");
                parameters.add(parameter);
            }
    
            // Add second parameter
            {
                final PayloadParameter parameter = new PayloadParameter();
                parameter.setId("2");
                parameter.setName("ccc");
                parameter.setValue("ddd");
                parameters.add(parameter);
            }
    
            // Set parameters list - will be wrapped in <parameters> element
            payload.setParameters(parameters);
    
            return payload;
        }
    }
    
    Output:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <payload xmlns="http://marshalling.jaxb.mtitek/payload">
        <id>1</id>
        <code>111</code>
        <parameters>
            <parameter id="1">
                <name>aaa</name>
                <value>bbb</value>
            </parameter>
            <parameter id="2">
                <name>ccc</name>
                <value>ddd</value>
            </parameter>
        </parameters>
    </payload>
    
  16. XML to Java Objects Unmarshalling Demo
    Test class demonstrating XML parsing with schema validation and object population from XML data.
    package mtitek.jaxb.marshalling;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Optional;
    
    import jakarta.xml.bind.JAXBException;
    import javax.xml.validation.Schema;
    
    import org.xml.sax.SAXException;
    
    import mtitek.jaxb.marshalling.payload.Payload;
    import mtitek.jaxb.utils.ResourceUtils;
    import mtitek.jaxb.utils.SchemaUtils;
    import mtitek.jaxb.utils.UnmarshallerUtils;
    
    /**
     * Test class demonstrating JAXB unmarshalling from XML to Java objects.
     */
    public class TestUnmarshaller {
    
        public static void main(String[] args) throws SAXException, IOException, JAXBException {
            // Print the schema content for reference
            System.out.println(ResourceUtils.getResourceAsString("payload.xsd"));
    
            final Optional<Schema> schema = SchemaUtils.getSchema("payload.xsd");
    
            if (schema.isPresent()) {
                try (final InputStream inputStream = ResourceUtils.getResourceAsStream("payload.xml")) {
                    // Unmarshal XML to Java object with schema validation
                    final Optional<Payload> optionalPayload = UnmarshallerUtils.unmarshal(
                        inputStream,
                        schema.get(),
                        Payload.class
                    );
    
                    if (optionalPayload.isPresent()) {
                        final Payload payload = optionalPayload.get();
                        System.out.println(payload);
    
                        // Print the parameters collection
                        System.out.println(payload.getParameters());
                    }
                }
            }
        }
    }
    
    Output:
    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema attributeFormDefault="unqualified"
        elementFormDefault="qualified"
        targetNamespace="http://marshalling.jaxb.mtitek/payload"
        xmlns="http://marshalling.jaxb.mtitek/payload"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.1">
    
        <xs:element name="payload">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="id" type="xs:string" />
    
                    <xs:element name="code" type="xs:string" />
    
                    <xs:element name="parameters" type="parametersType"
                        minOccurs="0" maxOccurs="1" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:complexType name="parametersType">
            <xs:sequence>
                <xs:element name="parameter" type="parameterType"
                    minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    
        <xs:complexType name="parameterType">
            <xs:all>
                <xs:element name="name" type="xs:string" />
                <xs:element name="value" type="xs:string" />
            </xs:all>
    
            <xs:attribute name="id" type="xs:string" />
        </xs:complexType>
    </xs:schema>
    
    Payload [id=123, code=xyz]
    [PayloadParameter [id=11, name=bar, value=foo], PayloadParameter [id=22, name=ta, value=ti]]
    
© 2025 mtitek