JSON (for %JSONValue% token) from delimited lines of text

Keyboard Maestro's %JSONValue% token provides (amongst other things) keyed access to any item you want, from a large set of values.

(Like Prompt with List, but without the need for UI interaction)

Data often arrives, however, as multiple lines of text, with each key and value delimited by a single character like a tab (%Tab% \t) or comma, or a multi-character delimiter like "__".

Here is a subroutine which constructs a JSON dictionary (readable by the %JSONValue% token) from a list of delimited lines.

JSONFromDelimitedLines.kmmacros (7,0 Ko)

An example of its use might be the following:


The ValuePrecedesKey option defaults to false / no / 0 if you leave it blank, and allows for cases where you want to search by keys in the 2nd column for values in the 1st column.

The default assumption is Key__Value (rather than Value__Key).

To choose that reversed option, you can supply any (case-insensitive) string drawn from true|yes|1


Expand disclosure triangle to view JS source of subroutine
const main = () => {
    const
        keyValueReversed = confirms(
            kmvar.local_ValuePrecedesKey
        );

    return JSON.stringify(
        lines(kmvar.local_Source)
            .reduce(
                (dict, row) => {
                    const
                        [key, value] = row.split(
                            kmvar.local_Delimiter
                        ),
                        update = keyValueReversed
                            ? { [value]: key }
                            : { [key]: value };

                    return Object.assign(dict, update);
                },
                {}
            ),
        null, 2
    );
};


// ----------------------- GENERIC -----------------------

// confirms :: String -> Bool
const confirms = v => {
    const s = v || "";

    return isNaN(s)
        ? ["Y", "T"].includes(s.toLocaleUpperCase()[0])
        : 0 < Number(s);
};


// lines :: String -> [String]
const lines = s =>
    // A list of strings derived from a single string
    // which is delimited by \n or by \r\n or \r.
    0 < s.length
        ? s.split(/\r\n|\n|\r/u)
        : [];

// MAIN ---
return main();
4 Likes

Hi @ComplexPoint
Curious to know, what is the advantage of using a JSON dictionary over a KM Dictionary? Is it that the JSON dictionary can be read and written to a text file saved anywhere I would like?

the JSON dictionary can be read and written to a text file saved anywhere I would like

Absolutely – it's a standard textual interchange format – a lot of online data is already in JSON format – and I personally find the usage inside Keyboard Maestro a bit more flexible and light-weight too.

2 Likes

Great. I will give this all a go.

In your example above is looks like to read from a key you use the syntax:

%JSONValue%local_Result{beta}% with beta being the key.

So, it is easy to write to a specific key in a JSON dictionary, like a KM Dictionary?

(Sorry, I know I can probably just work this out myself).

Not only to a specific key in a dictionary, but, (as JSON can represent highly nested data, in which some values are themselves arrays or dictionaries, and so on recursively) also to any specific JSON path.

JSON Paths

action:Set JSON Value [Keyboard Maestro Wiki]

Remember though, that (since all KM values are strings) any atomic value added from Keyboard Maestro will be interpreted as a String (rather than a Number, for example).

So:

Updated JSON Values.kmmacros (4.0 KB)

β†’

2 Likes

Thanks. That is all really interesting.

But KM can't read and write directly from/to a JSON file? It seems the file would have to first be read to a KM variable and then acted on?

I'm asking because my current workaround for KM Dictionaries only being saved on a local Mac, is to read and write the KM Dictionary to a JSON file which I save on Dropbox. I then have to read that file in and covert to KM Dictionary to use in a Macro and then reconvert to JSON at the end of the macro and save the JSON file. I was hoping the JSON file maybe could be accessed directly rather than having the steps of reading/writing it to a variable.

Absolutely, and that’s true of a programming or scripting language as well – reading in the current version and writing out an update.

1 Like

For simple (or very sophisticated) command-line operations on JSON files (singly or batched), it might be interesting to look, incidentally at the jq command-line processor https://jqlang.org

1 Like

If you did need to update specifically Numeric values, to obtain, for example:

{"Alpha":1,"Beta":2,"Gamma":[2,4,27,8]}

rather than

{"Alpha":1,"Beta":2,"Gamma":[2,4,"27",8]}

then you could use the jq command line processor rather than a Keyboard Maestro JSON Path:

NB – jq is on my system as /usr/bin/jq so I haven't given it a path in the macro below

(your mileage may vary – at the command line, try: which jq)

Updating JSON Values with the jq command line processor.kmmacros (4.9 KB)

(Note that jq and JS use 0-based indices into Arrays, whereas KM paths reserve [0] for holding the length of an array, and 1-based indices for its elements)