JQ слияние нескольких файлов JSON слияние любого элемента массива - PullRequest
0 голосов
/ 18 июня 2019

Я хочу использовать jq для объединения нескольких файлов, и если несколько файлов содержат массив с именем, мне нужно объединить массивы (порядок не имеет значения).

Например

file1

{
    "value1": 200,
    "timestamp": 1382461861,
    "parameter": [
    {"param": 1}
    ]
}

file2

{
    "status": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value3": "v3",
            "value4": 4
        }
    },
    "parameter" [
    {"param": 2}
    ]
}

для объединения этих json предлагается другие статьи переполнения стека, я должен сделать:

jq -s '.[0] * .[1]' file1 file2

пока это не получитсяя:

{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    {
      "param": 2
    }
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}

где то, что я хочу:

{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    { "param": 1}, 
    { "param": 2}
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}

обратите внимание, что массив "параметров" должен содержать элементы из файла file1 и file2

Мне также нужнорешение, которое не требует указания полей этого массива, а массивы могут быть вложены на любом уровне json

Я готов принять решение, которое не использует jq, небольшой скрипт на pythonвсе будет в порядке, я думаю,

самое близкое решение, которое я нашел, требует, чтобы я знал, что параметр - это массив

 jq -s '.[0] *  .[1]' file1.json file2.json >temp.json
 jq -s '.[0].parameter=([.[].parameter]|flatten)|.[0]' temp.json file1.json

, а вывод -

{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    {
      "param": 2
    },
    {
      "param": 1
    }
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}

Ответы [ 3 ]

1 голос
/ 19 июня 2019

Вот решение проблемы в случае, когда есть два файла, каждый с одним объектом, в котором есть хотя бы одно одноименное поле с массивом на некотором уровне.

Требования неясны по многим вопросам, поэтому может потребоваться настройка в соответствии с более подробными требованиями. Если существует более двух файлов, то можно использовать одни и те же методы, но опять же детали будут зависеть от подробных требований.

jq -n --slurpfile file1 file1.json --slurpfile file2 file2.json '

  # a and b are expected to be jq paths ending with a string
  # emit the array of the intersection of key names
  def common(a;b):
    ((a|map(.[-1])) + (b|map(.[-1]))) 
    | unique;

  $file1[0] as $f1
  | $file2[0] as $f2
  | [$f1 | paths as $p | select(getpath($p) | type == "array") | $p] as $p1
  | [$f2 | paths as $p | select(getpath($p) | type == "array") | $p] as $p2
  | $f1+$f2
  | if ($p1|length) > 0 and ($p2|length) > 0 
    then common($p1; $p2) as $both
    | if ($both|length) > 0 
      then first( $p1[] | select(.[-1] == $both[0])) as $p1
      |    first( $p2[] | select(.[-1] == $both[0])) as $p2
      | ($f1 | getpath($p1)) as $a1
      | ($f2 | getpath($p2)) as $a2
      | setpath($p1; $a1 + $a2)
      else .
      end
    else .
    end
  '

выход

Используя данный вход, после добавления пропущенного ":" во второй файл, вывод:

{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    {
      "param1": 1
    },
    {
      "param2": 2
    }
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}
1 голос
/ 19 июня 2019

Вы в основном реализуете свою собственную схему слияния. Если вам нужно обобщенное решение, вам нужно определить функцию, чтобы ее можно было сделать рекурсивно. Это будет не то же самое, что "*, но с другой семантикой массива", но вы можете использовать что-то вроде этого:

def new_merge($item):
    if type == ($item|type) then   # if same types
        if type == "array" then        # concatenate the arrays
            . + $item
        elif type == "object" then     # recursively merge objects
            reduce ($item|to_entries[]) as {$key,$value} (.;
                .[$key] |= new_merge($value)
            )
        else                           # just take the "other" value
            $item // .
        end
    else                           # just take the "other" value
        $item // .
    end
    ;

Я бы поместил это в ваш ~/.jq файл и вызвал бы так:

$ jq 'reduce inputs as $i (.; do_merge($i))' file*.json
{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    {
      "param": 1
    },
    {
      "param": 2
    }
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}

Если, с другой стороны, вы хотите, чтобы элементы массива рекурсивно объединялись, как это было в исходном вопросе, просто измените регистр массива, чтобы рекурсивно объединить соответствующие элементы.

def new_merge2($item):
    if type == ($item|type) then
        if type == "array" then
            [.,$item] | transpose[] as [$a,$b] | [$a | new_merge2($b)]
        elif type == "object" then
            reduce ($item|to_entries[]) as {$key,$value} (.;
                .[$key] |= new_merge2($value)
            )
        else
            $item // .
        end
    else
        $item // .
    end
    ;

Эта версия выдаст:

{
  "value1": 200,
  "timestamp": 1382461861,
  "parameter": [
    {
      "param1": 1,
      "param2": 2
    }
  ],
  "status": 200,
  "value": {
    "aaa": {
      "value3": "v3",
      "value4": 4
    }
  }
}
0 голосов
/ 19 июня 2019

Вот простое, но общее решение проблемы в случае двух файлов, каждый с одним объектом.

Это решение объединит каждую пару массивов на одном пути. Надеемся, что это достаточно просто, чтобы он говорил сам за себя и чтобы были внесены изменения для удовлетворения широкого круга более подробных требований.

jq -n --slurpfile file1 file1.json --slurpfile file2 file2.json '
  $file1[0] as $f1
  | $file2[0] as $f2
  | reduce ($f1 | paths) as $p ($f1+$f2;
      ($f1|getpath($p)) as $v1
      | ($f2|getpath($p)) as $v2
      | if ($v1 | type == "array") and
           ($v2 | type == "array")
        then setpath($p; $v1 + $v2)
        else .
        end)

'

Вывод выполняется точно так, как требуется, при условии, что второй файл был исправлен очевидным образом, так что это действительный JSON.

...