Formats

Five writers, one schema. They all carry the same information; they differ in how well they nest and how easily the receiving system reads them.

FormatOutputGood for
JSONdata/archive.jsonThe canonical, lossless master file
NDJSONdata/archive.ndjsonHuge sites — one object per line, nothing to hold in memory
XMLdata/archive.xmlSystems that speak XML; rich text is preserved in CDATA
YAMLdata/archive.yamlReading or hand-editing the model before an import
CSVdata/csv/*.csv + schema/*.csvSpreadsheets and simple importers

JSON

One document, and the format everything else is measured against. Read it and you have the whole bundle:

{
  "meta":    { … generator, source, format … },
  "schema":  { … the site's structure … },
  "records": {
    "entries":    [ … ],
    "categories": [ … ]
  }
}

Pick JSON unless you have a reason not to.

NDJSON

Newline-delimited JSON: one self-describing object per line, so a reader never has to hold the document in memory.

{"_type":"meta","meta":{…}}
{"_type":"schema","schema":{…}}
{"_type":"record","recordType":"entries","record":{…}}
{"_type":"record","recordType":"entries","record":{…}}

Every line names its own type, so an importer can stream the file and dispatch line by line. This is the format for a site large enough that json_decode on the whole document is a bad idea.

Reading one back? Don’t split on newlines naively — rich text values contain them. Use a JSON-aware line reader.

XML

Built with DOM, not string concatenation, so it’s well-formed by construction:

<archive formatVersion="1.0">
  <meta>…</meta>
  <schema>…</schema>
  <records>
    <record type="entries">…</record>
  </records>
</archive>

The conventions that make arbitrary Craft field handles safe to emit:

  • An object becomes one child element per key; a list becomes repeated <item> elements.
  • A key that isn’t a legal XML name becomes <item key="…">.
  • Null is <foo nil="true"/>; booleans are the strings true and false.
  • Values containing markup or newlines are wrapped in CDATA, so rich text survives intact.

YAML

The JSON document, in YAML, and structurally identical to it — same keys, same nesting. Worth choosing when a human is going to read the bundle before a machine does: reviewing what a migration is about to carry, or hand-editing the schema before feeding it to an importer.

CSV

CSV can’t nest, so this format is a set of files rather than one document:

data/csv/<type>.csv     one row per record, columns flattened to dotted keys
data/csv/relations.csv  one row per relation target, joining records by uid
schema/<key>.csv        the site's structure, which has nowhere else to go

The conventions:

  • Nested keys flatten to dotted columns: container.section, author.username, file.filename.
  • The header is the union of every row’s keys, so a column one record uses and another doesn’t still appears.
  • Multi-value cells use | as the separator.
  • Each field becomes a single fields.<handle> column. Relations collapse to a list of target uids — the full targets are in relations.csv. An option collapses to its value.
  • Anything that genuinely can’t flatten — Matrix blocks, table rows, raw — is JSON-encoded into its cell.
  • A record type that matched nothing is skipped rather than written as an empty file. The manifest still records the zero.
  • Quoting is plain RFC 4180. No backslash escaping.

Cells contain newlines. Parse with a real CSV reader, not by splitting lines.

Adding a format

Writers are registered by event, so a target-specific one — WordPress WXR, an in-house importer’s format — can be added from a module without touching Archive. See Extending.