Преобразование объектов в массивы пар ключ-значение - PullRequest
2 голосов
/ 29 января 2020

У меня есть файл, где каждая строка представляет собой json (не отформатирован) следующим образом:

{
  "callID": "xxxxxxxxxxxxxx",
  "authType": "xxxxxxxxxxxxxx",
  "timestamp": "xxxxxxxxxxxxxx",
  "errCode": "0",
  "errMessage": "xxxxxxxxxxxxxx",
  "endpoint": "xxxxxxxxxxxxxx",
  "userKey": "xxxxxxxxxxxxxx",
  "httpReq": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  },
  "ip": "xxxxxxxxxxxxxx",
  "params": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  },
  "uid": "xxxxxxxxxxxxxx",
  "apikey": "xxxxxxxxxxxxxx",
  "userAgent": {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  },
  "userKeyDetails": {
    "name": "xxxxxxxxxxxxxx"
  }
}

Мне нужно выполнить преобразование, где каждый объект (httpReq, params, userAgent, userKeyDetails) необходимо преобразовать как массив объектов со свойствами key и value. Каждый ключ не является обязательным, отдельный json может НЕ иметь всех заданных ключей.

Вот частичный вывод структуры:

{
  "httpReq": [
    {
      "key": "key1",
      "value": "value1"
    },
    {
      "key": "key2",
      "value": "value2"
    }
  ]
}

Использование jq командной строки, как я понимаю этот оператор to_entries является тем, который я ищу, поэтому я создал эту команду

cat test.json | jq -c '.userAgent = (.userAgent | to_entries) | .userKeyDetails = (.userKeyDetails | to_entries) | .params = (.params | to_entries) | .httpReq= (.
httpReq | to_entries)' > out.json

Она работает, но не работает в строках, где отсутствует один из указанных ключей, со следующей ошибкой:

jq: error (at <stdin>:2): null (null) has no keys
jq: error (at <stdin>:3): null (null) has no keys
jq: error (at <stdin>:4): null (null) has no keys
jq: error (at <stdin>:5): null (null) has no keys

Итак, мне нужен селектор, который работает с возможностью пропустить ключ, можно ли это получить непосредственно с помощью селектора jq?

Ответы [ 2 ]

4 голосов
/ 29 января 2020

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

map_values(if type == "object" then to_entries else . end)

REPL демо

0 голосов
/ 29 января 2020

Ваш начальный фильтр можно сократить до

(.userAgent |= to_entries) |
(.userKeyDetails |= to_entries) | 
(.params |= to_entries) | 
(.httpReq |= to_entries)

, и мы можем абстрагировать общее поведение функции:

def f(key):
  .[key] |= to_entries
;
f("userAgent") | f("userKeyDetails") | f("params") | f("httpReq")

Теперь нам просто нужно обновить f, чтобы он не завершится ошибкой, если запрошенный ключ не существует. Для этого мы можем заменить . на select(has(key)).

def f(key):
    <b>select(has(key))</b>[key] |= to_entries
;
f("userAgent") | f("userKeyDetails") | f("params") | f("httpReq")
...