Docs / Mapper Values

Build typed JSON from X12

Select business records, project the fields your receiving system needs, and serialize the result with toJson. Build dictionaries and lists directly so numbers, booleans, nested records, and null keep their types.

Project a record for each order line

lines = forEach(line in ST->PO1+ => {
  "itemNumber": requiredQualifierValue(line, "IN", 06),
  "quantity": line(02),
  "unitPrice": line(04),
  "extendedAmount": line(02) * line(04)
})
toJson({
  "purchaseOrderNumber": ST->BEG(03),
  "lines": lines,
  "lineCount": countOf(line in lines),
  "quantity": sumOf(line in lines => line["quantity"])
})

The projection arrow => computes one value per selected line. Here that value is a dictionary, so the result is a list of records. toJson handles quotes, escaping, commas, and numeric formatting.

X12 element definitions carry types. Quantities and prices become JSON numbers, including a transmitted price such as .58, which becomes 0.58. Item IDs, UPCs, line identifiers, and dates stay text. Use toNumber when you intentionally need numeric text as a number, such as a line-number sort key; keep the original identifier in the output.

Keep an absent reported amount as null

reportedTotal = ST->CTT?->AMT[01 == "TT"]?(02)
toJson({
  "totalAmount": isEmpty(reportedTotal) ? null : reportedTotal
})

An absent amount produces {"totalAmount":null}. A supplied zero produces {"totalAmount":0}. A reported total and a total calculated from lines are different business facts; choose deliberately which one your output represents.

null is a native scalar literal, not the string "null". It survives assignments, projections, dictionary fields, list entries, indexing, and JSON serialization. For example, toJson({"values":[null,0,false,"null"]}) produces {"values":[null,0,false,"null"]}.

Dictionary construction omits empty text and empty selections. Explicit null keeps the field. Missing X12 selections still have their existing empty-selection behavior; use a ternary when you want to translate absence into null.

Choose defaults without losing zero or false

isEmpty(null) is true, firstNonEmpty skips null, and required(null, "Total required") reports E2801. Zero and false are populated values. A nonempty list such as [null] is also present.

If every candidate is empty, firstNonEmpty returns empty text. Use a ternary for a null fallback; firstNonEmpty(value, null) does not retain null. Only supply zero before arithmetic when zero has the intended business meaning.

Null has no implicit numeric or Boolean conversion. toNumber(null) reports E4602, numeric arithmetic such as null * 2 reports E2402, and null ? 1 : 2 reports E2504. Supply an explicit default before calculations or sorting.

Compare absence explicitly

null == null is true. Null differs from empty text, empty selections, zero, false, and the text "null". Use isEmpty when you want to check the broader notion of absence.

Text conversion renders null as null. Concatenation with +, dictionary keys, and groupBy keys use that text. The text comparison operators retain their text rules: null %== "NULL" is true. Use ordinary == or != to distinguish null from text.

Practice one mapping pattern

Start with Map an 850 to JSON, then choose Write reliable incoming maps to practice typed records and optional values. The order-summary chapter builds on that model with aggregates, grouping, and sorting. The shipment chapter applies it to multiple orders and cartons using flatMap.

Continue with ordering, comparisons, and the Mapper Built-ins reference. Save representative variations as mapping test cases.