Skip to content

JSON Union → One CSV (heterogeneous keys)

View on GitHub

What

Merge several JSON exports whose objects carry different key sets into a single CSV with every column, where a key a record never had collapses to an empty cell.

Synthetic / teaching example

The data here is constructed, not sourced — two CRM dumps (crm_alpha.in.json with vat+email, crm_beta.in.json with phone). The problem class — JSON omits empty keys, so two dumps of "the same thing" are structurally different — is universal; the rows are not.

Why interesting

Unlike CSV, JSON has no fixed column list: every object only contains the keys it has a value for. Concatenate two API dumps and you get ragged objects — vat in one, phone in the other, email in some of both. Loading them into one table normally means a jq/pandas script to align the columns. bxp does the alignment from the output_schema alone.

flowchart LR
    A["crm_alpha.in.json<br/><small>vat · email</small>"] --> U["union<br/><small>output_schema · combined_output</small>"]
    B["crm_beta.in.json<br/><small>phone</small>"] --> U
    U --> C["1-json_union-combined.csvx<br/><small>company · vat · email · phone</small>"]

Problem class documented in. (sources for the problem class — not for the data)

  • The JSON object model has no schema; missing-vs-null is a perennial pain when flattening arrays of objects (the reason pandas.json_normalize and jq -s exist).

The trick

(see inline comments in sample.json):

Two mechanisms together — file_type_in: "json" (bxp takes the union of keys per file as that file's columns; an absent key reads as "") and combined_output: true (both files run through the one template into a single 1-json_union-combined.csvx). The output_schema lists every target column; each input normalises to it automatically.

Per-file outputs

bxp also writes a per-file <stem>.csvx; the combined file is the union result.

Final result

Two ragged JSON files become one rectangular CSVvat filled only for alpha rows, phone only for beta, email wherever it existed:

company,vat,email,phone
Acme s.r.o.,CZ12345678,sales@acme.cz,
Globex a.s.,CZ99887766,info@globex.cz,
Beta Ltd,,,555-0101
Initech,,hi@initech.io,555-0102

Sample data

Run it with bxp-cli --config ./sample.json --template json_union:

{
  // Teaching example — synthetic data. Two CRM exports arrive as JSON arrays
  // with DIFFERENT key sets: `crm_alpha` has vat + email, `crm_beta` has phone
  // (and email only on some records). JSON omits empty keys entirely, so the two
  // files are structurally heterogeneous. Goal: one CSV with every column, where
  // a key a record never had collapses to "".
  //
  // Two mechanisms combine here:
  //   * file_type_in:json — bxp takes the UNION of keys per file as that file's
  //     columns; a key absent from a record reads as "".
  //   * combined_output  — both files run through this one template into a single
  //     output, `1-json_union-combined.csvx`. The output_schema below IS the
  //     unified target schema; each input normalises to it automatically.
  conversion_templates: {
    json_union: {
      data_dir:         ".",
      file_type_in:     "json",
      file_pattern_in:  ".in.json",
      file_type_out:    "csv",
      file_pattern_out: ".csvx",
      combined_output:  true,

      input_schema: {
        $company: "[company]",
        $vat:     "[vat]",     // only in crm_alpha
        $email:   "[email]",   // in crm_alpha, and some crm_beta rows
        $phone:   "[phone]"    // only in crm_beta
      },

      row_rules: [ { when: "1 = 1", rows: [ {} ] } ],

      output_schema: {
        company: "$company",
        vat:     "$vat",
        email:   "$email",
        phone:   "$phone"
      }
    }
  }
}
[
  {"company":"Acme s.r.o.","vat":"CZ12345678","email":"sales@acme.cz"},
  {"company":"Globex a.s.","vat":"CZ99887766","email":"info@globex.cz"}
]
[
  {"company":"Beta Ltd","phone":"555-0101"},
  {"company":"Initech","phone":"555-0102","email":"hi@initech.io"}
]
company,vat,email,phone
Acme s.r.o.,CZ12345678,sales@acme.cz,
Globex a.s.,CZ99887766,info@globex.cz,
Beta Ltd,,,555-0101
Initech,,hi@initech.io,555-0102