# 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`](../guides/build-features.md) ·
**Apache Camel:** [JAXB](https://camel.apache.org/components/next/dataformats/jaxb-dataformat.html)

## 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](jacksonxml.md).

## Marshal and unmarshal

| direction | input body | output body |
|---|---|---|
| unmarshal | XML text or bytes | an object: element content keyed by child name, attributes as `@name` keys, values typed by the schema; with `schema` set the document is validated first |
| marshal | an 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

<!-- file: schemas/order.xsd -->
```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.

<!-- example: jaxb-unmarshal-typed -->
```yaml
- route:
    id: jaxb-unmarshal
    from:
      uri: direct:jaxb-unmarshal
      steps:
        - unmarshal:
            jaxb:
              contextPath: schemas/order.xsd
              schema: schemas/order.xsd
        - marshal:
            json: {}
```

<!-- input: jaxb-unmarshal-typed -->
```xml
<order id="7"><customer>ACME</customer><line><sku>A1</sku><qty>3</qty></line><express>true</express></order>
```

<!-- output: jaxb-unmarshal-typed -->
```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:

<!-- example: jaxb-reject-invalid -->
```yaml
- 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}"
```

<!-- input: jaxb-reject-invalid -->
```xml
<order id="8"><customer>ACME</customer><line><sku>A1</sku><qty>many</qty></line></order>
```

<!-- output: jaxb-reject-invalid {"contains": true} -->
```text
rejected:
'many' is not a valid value for 'int'
```

A valid order takes the other branch:

<!-- input: jaxb-reject-invalid#2 -->
```xml
<order id="9"><customer>ACME</customer><line><sku>A1</sku><qty>2</qty></line></order>
```

<!-- output: jaxb-reject-invalid#2 -->
```text
accepted order 9
```

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

<!-- example: jaxb-marshal -->
```yaml
- route:
    id: jaxb-marshal
    from:
      uri: direct:jaxb-marshal
      steps:
        - unmarshal:
            json: {}
        - marshal:
            jaxb:
              contextPath: schemas/order.xsd
              prettyPrint: false
              fragment: true
```

<!-- input: jaxb-marshal -->
```json
{"@id": 9, "customer": "Globex", "line": [{"sku": "B2", "qty": 1}, {"sku": "C3", "qty": 4}]}
```

<!-- output: jaxb-marshal -->
```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

| option | default | description |
|---|---|---|
| `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`. |
| `ignoreJAXBElement` | `true` | Unmarshal returns the root element's content; `false` wraps it as `{root: content}`. |
| `prettyPrint` | `true` | Indent the output, as Camel does. |
| `fragment` | `false` | Omit the XML declaration. |
| `encoding` | `UTF-8` | Encoding 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. |
| `typedDates` | `false` | Map `date`, `dateTime`, `time` and `duration` to calendar-like objects instead of strings. |
| `filterNonXmlChars` | `false` | Remove characters that are illegal in XML. |
| `contentTypeHeader` | `true` | Set `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](../components/validator.md).

### 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.

## Related

- [Jackson XML data format](jacksonxml.md): schema-less XML mapping
- [Validator component](../components/validator.md), [XSLT component](../components/xslt.md)
- [Apache Camel JAXB data format](https://camel.apache.org/components/next/dataformats/jaxb-dataformat.html)

## 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](https://github.com/pmsudhi/cameleer/issues/new) 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](https://github.com/pmsudhi/cameleer/blob/main/CONTRIBUTING.md)
explains the ground rules.
