Convert key = value pairs to JSON

Viewed 794

I am trying to convert a file containing key = value pairs into JSON. This file might contain Windows EOL (\r\n) and empty lines.

Given the following input (mind the empty lines):

foo = aa
bar = bb

qux = cc
white space = white space
* = special-char


This is the expected result:

{
  "foo": "aa",
  "bar": "bb",
  "qux": "cc",
  "white space": "white space",
  "*": "special-char"
}

I managed to go this far:

{
  "foo": "aa"
}
{
  "bar": "bb"
}
{
  "qux": "cc"
}
{
  "white space": "white space"
}
{
  "*": "special-char"
}

Using the following command:

 jq --raw-input 'split("\n") | map(split(" = ") | { (.[0]): .[1] }) | .[]' 

But I can not figure out the missing bit. What is missing or is this a better way to achieve this?

Edit: added constraint about empty line and Windows EOL

2 Answers

You were really close. Drop \rs at the end, split by =, transform resulting arrays into objects, put them into an array and pass it to add.

[ inputs
  | gsub("\r$"; "")
  | split(" = "; "")
  | select(length == 2)
  | {(.[0]): .[1]}
] | add

You need --raw-input/-R and --null-input/-n options specified on the command line in order for this to work.

If one of the "values" contains an "=" then using split("=") naively will produce incorrect results. If your jq supports capture, then here is a solution that avoids that issue:

jq -nR '
  def trim: sub("^ +";"") | sub(" +$";"");
  [inputs
   | select(index("="))
   | sub("\r$"; "")
   | capture( "(?<key>[^=]*)=(?<value>.*)" )
   | ( (.key |= trim) | (.value |= trim)) ]
  | from_entries'
Related