TCL Dict to JSON

Viewed 819

I am trying to convert a dict into JSON format and not seeing any easy method using TclLib Json Package. Say, I have defined a dict as follows :

set countryDict [dict create USA {population 300 capital DC} Canada {population 30 capital Ottawa}]

I want to convert this to json format as shown below:

{
  "USA": {
    "population": 300,
    "captial": "DC"
  },
  "Canada": {
    "population": 30,
    "captial": "Ottawa"
  }
}

("population" is number and capital is string). I am using TclLib json package (https://wiki.tcl-lang.org/page/Tcllib+JSON) . Any help would be much appreciated.

1 Answers

There's two problems with the “go straight there” approach that you appear to be hoping for:

  1. Tcl's type system is extremely different to JSON's; in Tcl, every value is a (subtype of) string, but JSON expects objects, arrays, numbers and strings to wholly different things.
  2. The capital becomes captial. For bonus fun. (Hopefully that's just a typo on your part, but we'll cope.)

I'd advise using rl_json for this; it's a much more capable package that treats JSON as a fundamental type. (It's even better at it when it comes to querying into the JSON structure.)

package require rl_json

set result {{}};  # Literal empty JSON object
dict for {countryID data} $countryDict {
    rl_json::json set result $countryID [rl_json::json template {{
        "population": "~N:population",
        "captial": "~S:capital"
    }} $data]
    # Yes, that was {{ … }}, the outer ones are for Tcl & the inner ones for a JSON object
}
puts [rl_json::json pretty $result]

That produces almost exactly the output you asked for, except with different indentation. $result is the “production” version of the output that you can work with for further processing, but which has no excess whitespace at all (which is a great choice when you're dealing with documents over 100MB long).


Notes:

  • The initial JSON object could have been done like this:

    set result "{}"
    

    that would have worked just as well (and been the same Tcl bytecode).

  • json set puts an item into an object or array; that's exactly what we want here (in a dict for to go over the input data).

  • json template takes an optional dictionary for mapping substitution names in the template to values; that's perfect for your use case. Otherwise we'd have had to do dict with data {} to map the contents of the dictionary into variables, and that's less than perfect when the input data isn't strictly controlled.

  • The contents of template argument to json template is itself JSON. The ~N: prefix in a leaf string value says “replace this with a number from the substitution called…”, and ~S: says “replace this with a string from the substitution called…”. There are others.

Related