Cameleer

JAXB data format

The jaxb data format converts XML to structured values and back, typed and optionally validated by an XML Schema (XSD), without generated Java classes.

Name: jaxb · Crate: cameleer-soap · Cargo feature: soap · Apache Camel: JAXB

When to use it

  • Read and write XML documents that have an XSD: orders, invoices (UBL), bank statements (ISO 20022), SOAP payloads.
  • Get typed values from XML: numbers, booleans and dates come out typed according to the schema, and repeated elements are always arrays even when only one is present.
  • Validate XML on the way in or out and reject documents that break the contract, for example XML generated by an AI agent before it reaches a partner.

With cameleer_main::new_main() and the container image (which register cameleer-soap), jaxb is this schema-typed binding. With cameleer-core alone, jaxb is an alias of the schema-less Jackson XML data format.

Marshal and unmarshal

directioninput bodyoutput body
unmarshalXML text or bytesan object: element content keyed by child name, attributes as @name keys, values typed by the schema; with schema set the document is validated first
marshalan object (or a single-key object naming the root element)XML text, indented by default, with an XML declaration unless fragment=true

The root element on marshal is partClass, else the single key of the body object if it names a global element, else the single global element of the schema. Without any schema the mapping is structural: text-only elements become strings, repeated siblings arrays, attributes @-prefixed keys. Validation errors raise UnmarshalException or MarshalException (both JAXBException).

Example

xml
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="order">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="customer" type="xs:string"/>
        <xs:element name="line" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="sku" type="xs:string"/>
              <xs:element name="qty" type="xs:int"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="express" type="xs:boolean" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:int" use="required"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

Unmarshal and validate an order. qty is an integer and line an array even with one line, so the JSON is ready for an API or a prompt.

yaml example: jaxb-unmarshal-typed
- route:
    id: jaxb-unmarshal
    from:
      uri: direct:jaxb-unmarshal
      steps:
        - unmarshal:
            jaxb:
              contextPath: schemas/order.xsd
              schema: schemas/order.xsd
        - marshal:
            json: {}
xml
<order id="7"><customer>ACME</customer><line><sku>A1</sku><qty>3</qty></line><express>true</express></order>
json
{"@id":7,"customer":"ACME","line":[{"sku":"A1","qty":3}],"express":true}

An invalid document fails with UnmarshalException. Catch it to answer the sender instead of failing the exchange:

yaml example: jaxb-reject-invalid
- route:
    id: jaxb-reject
    from:
      uri: direct:jaxb-reject
      steps:
        - doTry:
            steps:
              - unmarshal:
                  jaxb:
                    contextPath: schemas/order.xsd
                    schema: schemas/order.xsd
              - setBody:
                  simple: "accepted order ${body[@id]}"
            doCatch:
              - exception:
                  - UnmarshalException
                steps:
                  - setBody:
                      simple: "rejected: ${exception.message}"
xml
<order id="8"><customer>ACME</customer><line><sku>A1</sku><qty>many</qty></line></order>
text
rejected:
'many' is not a valid value for 'int'

A valid order takes the other branch:

xml
<order id="9"><customer>ACME</customer><line><sku>A1</sku><qty>2</qty></line></order>
text
accepted order 9

Marshal JSON into XML with the root element from the schema:

yaml example: jaxb-marshal
- route:
    id: jaxb-marshal
    from:
      uri: direct:jaxb-marshal
      steps:
        - unmarshal:
            json: {}
        - marshal:
            jaxb:
              contextPath: schemas/order.xsd
              prettyPrint: false
              fragment: true
json
{"@id": 9, "customer": "Globex", "line": [{"sku": "B2", "qty": 1}, {"sku": "C3", "qty": 4}]}
xml
<order id="9"><customer>Globex</customer><line><sku>B2</sku><qty>1</qty></line><line><sku>C3</sku><qty>4</qty></line></order>

Rust DSL

rust
b.from("direct:jaxb-rust")
    .unmarshal(DataFormatDefinition::new("jaxb").option("contextPath", "schemas/order.xsd"))
    .marshal_json();

Options

optiondefaultdescription
contextPath(none)Schema(s) used for typing: XSD locations separated by , or ; (path, file:, classpath:, #bean, or inline XSD), or a .properties descriptor with schema= and prefix.<p>=<namespace> lines.
schema(none)XSD used to validate on unmarshal and marshal (also used for typing when contextPath gives none).
partClass(none)Root element for marshal: {namespace}local or local (with partNamespace).
partNamespace(none)Namespace of partClass.
ignoreJAXBElementtrueUnmarshal returns the root element's content; false wraps it as {root: content}.
prettyPrinttrueIndent the output, as Camel does.
fragmentfalseOmit the XML declaration.
encodingUTF-8Encoding of the declaration and output (ISO-8859-1 supported for bytes).
namespacePrefix(none)Preferred prefixes: uri=prefix,uri2=prefix2 or a #bean map.
attributePrefix@Key prefix for attributes; plainAttributes=true uses plain keys.
typedDatesfalseMap date, dateTime, time and duration to calendar-like objects instead of strings.
filterNonXmlCharsfalseRemove characters that are illegal in XML.
contentTypeHeadertrueSet Content-Type on marshal.

mustBeJAXBElement, objectFactory, noNamespaceSchemaLocation, schemaLocation, schemaSeverityLevel, jaxbProviderProperties and xmlStreamWriterWrapper are accepted for compatibility.

Differences from Apache Camel

Camel binds XML to classes generated by xjc (contextPath is a Java package). Here contextPath points at the XSD itself and the result is a plain object. Polymorphism follows the schema: xsi:type selects the derived type (kept as @xsi:type), and substitution group members bind where their head element is declared.

Common questions

How do I validate XML against an XSD while parsing it?

Set schema: path/to.xsd on the jaxb unmarshal (as in the example). For validation alone, without conversion, use the validator component.

How do I convert XML to JSON with correct number types?

unmarshal: jaxb: {contextPath: my.xsd} then marshal: json: {}. Without a schema every value is a string, because XML has no types of its own.

Why is a single element still an array?

The schema says maxOccurs="unbounded", so the binding always produces an array. That keeps code that reads the value simple.

Found a problem?

If something on this page doesn't match what Cameleer does, or you hit a bug while building with it, please help fix it, whether you are a person or a coding agent: open an issue that describes what you saw, then fork the repository, fix it, add or update a test, run cargo test --workspace, and open a pull request. CONTRIBUTING.md explains the ground rules.

Edit this page on GitHub