web-dev-qa-db-ja.com

jqを使用してJSONドキュメントをフラット化する

次のJSONオブジェクトの配列を検討しています。

[
  {
    "index": "index1",
    "type": "type1",
    "id": "id1",
    "fields": {
      "deviceOs": [
        "Android"
      ],
      "deviceID": [
        "deviceID1"
      ],
      "type": [
        "type"
      ],
      "country": [
        "DE"
      ]
    }
  },
  {
    "index": "index2",
    "type": "type2",
    "id": "id2",
    "fields": {
      "deviceOs": [
        "Android"
      ],
      "deviceID": [
        "deviceID2"
      ],
      "type": [
        "type"
      ],
      "country": [
        "US"
      ]
    }
  }
]

そして私はそれを得るためにそれを平らにしたいと思います:

[
  {
    "index": "index1",
    "type": "type",
    "id": "id1",
    "deviceOs": "Android",
    "deviceID": "deviceID1",
    "country": "DE"
  },
  {
    "index": "index2",
    "type": "type",
    "id": "id2",
    "deviceOs": "Android",
    "deviceID": "deviceID2",
    "country": "US"
  }
]

jqを使用しようとしていますが、"fields"をフラット化できません。どうすればよいですか?現時点ではコマンドラインツールに興味がありますが、他の提案も受け入れます。

19
Dror

これは細工が難しいものでした。

map
(
    with_entries(select(.key != "fields"))
    +
    (.fields | with_entries(.value = .value[0]))
)

それを分解して、そのビットを説明しましょう

  1. 配列内のすべてのアイテムについて...

    map(...)
    
  2. fieldsプロパティを除くすべての値を含む新しいオブジェクトを作成します。

    with_entries(select(.key != "fields"))
    
  3. それを組み合わせる...

    +
    
  4. fieldsは、各配列の最初の項目に各値を射影します

    (.fields | with_entries(.value = .value[0]))
    
31
Jeff Mercado

このフィルターを使用できます。

[.[] | {index: .index, type: .type, id: .id, deviceOs: .fields.deviceOs[],deviceID: .fields.deviceID[],country: .fields.country[]}]

ここでテストできます https://jqplay.org

5
aborruso

以下は、+を使用して.fieldsを含むオブジェクトにマージし、次に配列要素を平坦化することから始まるいくつかのバリエーションです。まず、.fieldsを

  .[]
| . + .fields
| del(.fields)

それは私たちのように見えるオブジェクトを残します

{
  "index": "index1",
  "type": [
    "type"
  ],
  "id": "id1",
  "deviceOs": [
    "Android"
  ],
  "deviceID": [
    "deviceID1"
  ],
  "country": [
    "DE"
  ]
}

次に、キーを複数の方法でフラット化できます。 1つの方法はwith_entriesを使用することです

| with_entries( .value = if .value|type == "array" then .value[0] else .value end )

別の方法はreducesetpathを使用することです

| . as $v
| reduce keys[] as $k (
    {};
    setpath([$k]; if $v[$k]|type != "array" then $v[$k] else $v[$k][0] end)
  )
1
jq170727

gron というツールがあり、そのjsonをパイプして、このようなものを取得できます。

$ gron document.json
json = [];
json[0] = {};
json[0].fields = {};
json[0].fields.country = [];
json[0].fields.country[0] = "DE";
json[0].fields.deviceID = [];
json[0].fields.deviceID[0] = "deviceID1";
json[0].fields.deviceOs = [];
json[0].fields.deviceOs[0] = "Android";
json[0].fields.type = [];
json[0].fields.type[0] = "type";
json[0].id = "id1";
json[0].index = "index1";
json[0].type = "type1";
json[1] = {};
json[1].fields = {};
json[1].fields.country = [];
json[1].fields.country[0] = "US";
json[1].fields.deviceID = [];
json[1].fields.deviceID[0] = "deviceID2";
json[1].fields.deviceOs = [];
json[1].fields.deviceOs[0] = "Android";
json[1].fields.type = [];
json[1].fields.type[0] = "type";
json[1].id = "id2";
json[1].index = "index2";
json[1].type = "type2";

1
sunapi386