Преобразовать текстовый файл с разрывами строк в json с помощью jq - PullRequest
1 голос
/ 27 февраля 2020

Я видел много примеров преобразования текстового файла в json с помощью jq, но я застрял на чем-то, очевидно, очевидном. Мой входной файл имеет следующий формат:

key1: string1
key2: string1

key1: string3
key2: string3

Как мне перевести это на:

[
  {"key1":"string1", "key2": "string2"},
  {"key1":"string3", "key2": "string4"}
]

Я пытался использовать inputs с jq, что-то вроде jq -R -n -c '[inputs|split(":")|{(.[0]):.[1]}] | add', но происходит сбой, как только в файле появляется разрыв строки:

jq: error (at result.txt:8): Cannot use null (null) as object key.

Спасибо

Ответы [ 3 ]

1 голос
/ 27 февраля 2020

Вот краткое, но довольно надежное решение, предполагающее, что jq вызывается с опциями -n и -R (например, jq -nR ...)

[foreach (inputs, null) as $in (null;
  if ($in|length) == 0
  then .out = .object | .object = null
  else .object += ($in | capture("(?<k>^[^:]*): *(?<v>.*)") | {(.k):.v})
  | .out = null
  end;
  .out // empty) ]

Здесь одна ключевая точка (без каламбура) использование capture вместо split или splits в случае, если значение содержит двоеточие. Обратите внимание также на использование идиомы foreach (inputs,null) as .... Это гарантирует, что последний ключ: ввод значения правильно обрабатывается.

1 голос
/ 27 февраля 2020

Поскольку сокращение широко понимается, вот простое решение на основе reduce, которое не требует перевода строки между группами ключ: строки значения. То есть при построении массива новый объект запускается при обнаружении ключа, который встречается в предыдущем объекте.

< input.txt jq -nR '
  reduce (inputs
          | select(length > 0) 
          | capture("(?<k>^[^:]*): *(?<v>.*)") 
          | {(.k):.v}) as $in (null;
    if . == null then [$in] 
    elif (.[-1] | has($in|keys_unsorted[0])) then . + [$in] 
    else .[-1] += $in end)'
1 голос
/ 27 февраля 2020

Это довольно хороший вариант использования редуктора.

Запустите ваш документ на своем стандартном диске, скрипт:

#!/usr/bin/env jq -Rncf
# ...caveat: not all platforms support more than two words on the shebang
#            you may need to stop using env and pass a full path to your jq binary!

def input_line_to_object:
  "^(?<key>[^:]+): ?(?<value>.*)$" as $re |
  (capture($re) // ("line \(.) does not parse" | halt_error(1))) | {(.key):.value};

reduce (inputs, "") as $item (
  # Initializer. .[0] == current object; .[1] == list of objects already constructed
  [{}, []];
  # Handler. Run for each line, and the final "" at the end.
  if $item == "" then
    if .[0] == {} then
      . # no new object is actually ready; maybe we had two newlines in a row?
    else
      # between objects: append .[0] to .[1], and clear .[0]
      [{}, .[1]+[.[0]]]
    end
  else
    # new data in an existing object; add new datum to .[0], leave .[1] alone
    [.[0] + ($item | input_line_to_object), .[1]]
  end
) | .[1] # take the completed list in the .[1] position

... выдает в качестве вывода:

[{"key1":"string1","key2":"string1"},{"key1":"string3","key2":"string3"}]
...