Data Formats

Programs read and write documents other programs made: an API's JSON, a spreadsheet's CSV, a service's TOML or YAML settings. Each format has a type with a parse that gives plain values and a decode that gives the program's own types - checked field by field, with an error that says exactly where a document went wrong.

JSON into the program's types

Json.decode<Order> reads the document, then builds an Order from it the way Order's declarations say: each field from the object's member of that name, each nested object into its field's struct, each array item into the array's element type. Numbers are checked against their type's range, so a U16 field given -2 is a ParseError, not a wrapped number. Text becomes whatever FromString type the field has - Decimal, Date, Uuid, Url, Duration, Version - and a unit enum case is read from its name. A field whose type takes undefined may be missing; any other missing field is an error. Members the struct has no field for are ignored.

How a type's fields are found is Type.fields() - the same reflection a program can use itself.

Plain values

Json.parse gives a document's values as they are: an object a Map<String, Unknown> in its members' order, an array an Array<Unknown>, a number an F64, and nullundefined. Json.encode writes any value back - a struct as an object of its fields - on one line, or indented when given an indent. What #JSON {{{ ... }}} makes at compile time is covered in Macro Namespaces.

CSV

Csv.parse follows RFC 4180: a field in double quotes may hold the separator, line breaks, and quotes written twice. records pairs each row with the header row's names, and decode<Row> goes on into a Table<Row>: each column into the field it names, parsed as that field's type, an empty field undefined for an optional one. write quotes exactly the fields that need it. Every function takes another separator - Csv.parse(text, ';') for the files some spreadsheets write.

TOML and YAML

Toml reads TOML 1.0 - tables, arrays of tables, inline tables, dotted keys, every kind of string, integers in hex, octal and binary, and dates and times, which come back as Date, TimeOfDay, DateTime, or an Instant when they carry an offset. Yaml reads the YAML configuration files are written in: block mappings and sequences by indentation, [flow] and {flow} collections, plain and quoted scalars, and | / > block text. Anchors, aliases and tags are a ParseError rather than read wrongly. Both decode the way JSON does, so one settings struct reads from either.

Hashes and compression

Bytes has the hashes programs need to agree with others on: sha256, hmacSha256 for signing, sha1 and md5 where an older protocol asks for them, crc32 and the fast fnv1a for checksums and keys. toBase64Url writes bytes so they fit in a URL or a file name. compress makes DEFLATE data - a .gz file's bytes by default, or a zlib stream, or raw - and decompress reads what any compressor made, checking its checksum.