Skip to content

Dataformat processors

pkg/processor/dataformat.go provides marshal/unmarshal pairs for the common wire formats (JSON, XML, CSV, YAML) and content-encoding processors (Base64, GZip). They are simple Message Translator EIPs and back the data-format step variants exposed via the DSL.

The factory function GetDataFormat(name) returns a marshal/unmarshal pair by name for use in custom code.

Processors in this file

JSON: JSONMarshal / JSONUnmarshal

Direction Body in Body out Content-Type set
Marshal any []byte (JSON) application/json
Unmarshal []byte or string parsed any (map[string]any, []any, float64, etc.) none

Both use encoding/json. No struct field tags are involved — marshaling reflects whatever Go value is in the body.

XML: XMLMarshal / XMLUnmarshal

Direction Body in Body out Content-Type set
Marshal map[string]any (preferred) or any []byte (XML) application/xml
Unmarshal []byte map[string]any keyed by root element none

XMLMarshal has one configurable field:

Field Type Default Description
RootElement string "root" Wrapper tag when the body is a map.

Maps marshal as nested <key>…</key> elements with deterministic (sorted) key order. Slices of maps marshal as <item> elements. Other types fall through to xml.Marshal.

XMLUnmarshal produces a map[string]any:

  • Text-only elements become string.
  • Element with children becomes map[string]any.
  • Duplicate child tags accumulate into a []any.

CSV: CSVMarshal / CSVUnmarshal

Field Type Default Description
Delimiter rune , Cell separator.
Header bool true (via factory) Marshal: write first row as header. Unmarshal: parse first row as header and return []map[string]any.

Accepted body types for CSVMarshal: []map[string]any (deterministic header order via sorted keys) or [][]string (raw row matrix). Anything else returns an error.

CSVUnmarshal with Header: false returns a [][]string.

YAML: YAMLMarshal / YAMLUnmarshal

Uses gopkg.in/yaml.v3. Symmetric with JSON — body in is any Go value; body out is []byte for marshal, any for unmarshal. Marshal sets Content-Type: application/yaml.

Base64: Base64Encode / Base64Decode

Uses encoding/base64.StdEncoding. Both operate on []byte. Encode writes the encoded bytes back as the body; Decode parses the body as a base64 string and writes the decoded bytes.

GZip: GZipCompress / GZipDecompress

GZipCompress reads the body as bytes, compresses with compress/gzip, writes the compressed bytes back, and sets Content-Encoding: gzip.

GZipDecompress has one safety-tuning field:

Field Type Default Description
MaxDecompressedSize int64 100 MiB (100 × 1024 × 1024) Cap on decompressed output. Prevents decompression bombs.

Decompression reads at most MaxDecompressedSize + 1 bytes via io.LimitReader; if the read returns more, the processor errors with decompressed size exceeds limit of N bytes.

GetDataFormat

Factory used by the DSL reifier. Returns (marshal, unmarshal, err) for these names:

Name Marshal Unmarshal
json &JSONMarshal{} &JSONUnmarshal{}
xml &XMLMarshal{} (root "root") &XMLUnmarshal{}
csv &CSVMarshal{Delimiter:',', Header:true} &CSVUnmarshal{Delimiter:',', Header:true}
yaml &YAMLMarshal{} &YAMLUnmarshal{}
base64 &Base64Encode{} &Base64Decode{}
gzip &GZipCompress{} &GZipDecompress{}

Unknown names return unknown data format: <name>.

Pitfalls

  • XML unmarshal returns text content as plain string only when an element has no child elements; mixed-content elements become a map[string]any with the text discarded. Don't rely on the XML processor for documents with significant whitespace.
  • CSV marshal of []map[string]any uses sorted keys of the first row as header. If later rows have additional keys, those values are silently dropped.
  • Base64 uses standard encoding (with +/=). URL-safe encoding is not exposed via the YAML factory — call encoding/base64.URLEncoding directly in Go if needed.
  • GZipDecompress returns an error rather than truncating when output exceeds MaxDecompressedSize. Tune this for legitimate large payloads.

Examples

Marshal a map to JSON, then to base64, then publish:

workflows:
  - id: encode-event
    trigger: { uri: direct://events }
    steps:
      - transform:
          starlang: |
            %sl 2.0
            output application/json
            input payload
            ---
            { "id": payload.id, "ts": now() }
      - process: { ref: json-marshal }
      - process: { ref: base64-encode }
      - target: kafka://events-b64

For custom code, use the factory:

marshal, unmarshal, err := processor.GetDataFormat("csv")

Source

pkg/processor/dataformat.go

See also

  • transform — expression-driven body rewrites (use for non-format-specific transforms).
  • convert — ConvertBody for simple type coercions like string ↔ []byte ↔ map.
  • streaming_json — stream large JSON arrays without holding them all in memory.