いくつかのLinuxコマンドで巨大な古いjsonを新しいjson形式に変換しますか?

いくつかのLinuxコマンドで巨大な古いjsonを新しいjson形式に変換しますか?

私はこのような生のJSONを持っています。一般的に私のjsonは大きいです。内容をより簡単かつ簡単に理解するためにアイテムを減らしました。

{
   "clientSettings":[
      {
         "clientId":12345,
         "key":"abc",
         "value":false
      },
      {
         "clientId":12345,
         "key":"def",
         "value":false
      },
      {
         "clientId":12345,
         "key":"ghi",
         "value":false
      },
      {
         "clientId":9876,
         "key":"lkmn",
         "value":false
      }
   ],
   "productSettings":[
      {
         "productId":11,
         "key":"jkl",
         "value":true
      },
      {
         "productId":11,
         "key":"mno",
         "value":true
      },
      {
         "productId":12,
         "key":"jkl",
         "value":true
      },
      {
         "productId":12,
         "key":"mno",
         "value":true
      }
   ],
   "customerSettings":[
      {
         "key":"enableData",
         "value":false
      },
      {
         "key":"minPriceValue",
         "value":"1.0"
      },
      {
         "key":"presentData",
         "value":"AEGIS"
      }
   ],
   "thothTest":{
      "9876":[
         "K"
      ],
      "5431":[
         "A",
         "L"
      ],
      "5123":[
         "L"
      ]
   },
   "osirisTest":{
      "7678":[
         "K"
      ]
   }
}
  • clientSettingsjson配列にはclientId'sキー/値があります。 Single の場合、clientId複数の異なるキーと値を持つことができます。たとえば、12345 clientId上記のように異なるキーと値を使用します。
  • 同様productSettingsにについても同様である。
  • しかし、customerSettings他のキーと値があるからです。
  • なぜなら何もthothTestするosirisTest必要がないからです。

clientId各キーと値について、およびを繰り返す必要がないように、上記のjsonを再設計するつもりです。productIdこれまで私のjsonは同じですが、キーと値が異なるため、とても素晴らしいですids

だから私は上記のjsonを表現できる新しいjsonデザインを思いつきました。

{
   "clientSettings":[
      {
         "clientId":12345,
         "entries":[
            {
               "key":"abc",
               "value":false
            },
            {
               "key":"def",
               "value":false
            },
            {
               "key":"ghi",
               "value":false
            }
         ]
      },
      {
         "clientId":9876,
         "entries":[
            {
               "key":"lkmn",
               "value":false
            }
         ]
      }
   ],
   "productSettings":[
      {
         "productId":11,
         "entries":[
            {
               "key":"jkl",
               "value":true
            },
            {
               "key":"mno",
               "value":true
            }
         ]
      },
      {
         "productId":12,
         "entries":[
            {
               "key":"jkl",
               "value":true
            },
            {
               "key":"mno",
               "value":true
            }
         ]
      }
   ],
   "customerSettings":[
      {
         "key":"enableData",
         "value":false
      },
      {
         "key":"minPriceValue",
         "value":"10.28"
      },
      {
         "key":"presentData",
         "value":"AEGIS"
      }
   ],
   "thothTest":{
      "9876":[
         "K"
      ],
      "5431":[
         "A",
         "L"
      ],
      "5123":[
         "L"
      ]
   },
   "osirisTest":{
      "7678":[
         "K"
      ]
   }
}

問題の説明

これで古いjsonが提供されます。一種のスクリプトやLinuxコマンドを使ってそれを新しいjson形式に変換する方法はありますか?私のjsonは非常に大きいので、各IDを1つずつ処理するのに時間がかかりました。

答え1

jqさまざまな方法でJSONを再構成できます。 1つの方法は、グループ化された配列の外側にある.clientId新しいオブジェクトをグループ化してマッピングすることです。.clientId使用group_bymap:

jq ' .clientSettings |=
    ( group_by(.clientId) |
      map( {clientId: .[0].clientId, entries: del(.[].clientId)} )
    ) |
     .productSettings |=
    ( group_by(.productId) |
      map( {productId: .[0].productId, entries: del(.[].productId)} )
    ) ' file.json

2番目のオブジェクトに対して同じコマンドをコピーして貼り付けました。


大規模なJSONを頻繁に作業する必要がある場合は、mongodbまたは同様のドキュメントデータベースに精通して使用することをお勧めします。たとえば、次を参照してください。地図拡大ページマニュアルを見ると、縮小された部分なしで今やっていることがそうです。これらの操作はすべてjsonファイルを解析するよりも速く、構文は単純なjavascriptに似ています。

答え2

JSON処理は、JSON(Pythonなど)を理解するツールを使用して実行するのが最善ですjq。必要なタスクを実行するPythonスクリプトは次のとおりです。

#! /usr/bin/env python3

import json
import sys
from collections import defaultdict


def combine(data, key, id_key):
    new_settings = defaultdict(list)
    for setting in data[key]:
        # Remove the ID from the setting and add it to the list of settings for that ID
        new_settings[setting.pop(id_key)].append(setting)
    # arrange the new settings in the desired format and overwrite the old settings
    data[key] = [{id_key: key, "entries": values} for key, values in new_settings.items()]


data = json.load(sys.stdin)
combine(data, "clientSettings", "clientId")
combine(data, "productSettings", "productId")
print(json.dumps(data))

標準入力にJSONを入力し、出力を使用します。

$ ./process.py < old.json | jq
{
  "clientSettings": [
    {
      "clientId": 12345,
      "entries": [
        {
          "key": "abc",
          "value": false
        },
        {
          "key": "def",
          "value": false
        },
        {
          "key": "ghi",
          "value": false
        }
      ]
    },
    {
      "clientId": 9876,
      "entries": [
        {
          "key": "lkmn",
          "value": false
        }
      ]
    }
  ],
  "productSettings": [
    {
      "productId": 11,
      "entries": [
        {
          "key": "jkl",
          "value": true
        },
        {
          "key": "mno",
          "value": true
        }
      ]
    },
    {
      "productId": 12,
      "entries": [
        {
          "key": "jkl",
          "value": true
        },
        {
          "key": "mno",
          "value": true
        }
      ]
    }
  ],
  "customerSettings": [
    {
      "key": "enableData",
      "value": false
    },
    {
      "key": "minPriceValue",
      "value": "1.0"
    },
    {
      "key": "presentData",
      "value": "AEGIS"
    }
  ],
  "thothTest": {
    "9876": [
      "K"
    ],
    "5431": [
      "A",
      "L"
    ],
    "5123": [
      "L"
    ]
  },
  "osirisTest": {
    "7678": [
      "K"
    ]
  }
}

答え3

私が答えた解決策は基本的に次のとおりです。出版社:Sanasipしかし、ヘルパー関数を使用してgroupmap配列の内容をグループ化してマッピングするように書き直されました。clientSettingsproductSettings

def groupmap($key):
        group_by(.[$key]) |
        map( { ($key): first[$key], entries: map(del(.[$key])) } );

.clientSettings  |= groupmap("clientId") |
.productSettings |= groupmap("productId")

これを使用すると、script次のコマンドを使用して実行されます。

jq -f script file.json

以下は必須ではありませんが、複数の配列に「アイテムオブジェクト」(keyおよびvalue)が含まれているため、すべてのアイテムをkey: valueアイテムに変換できます。

def groupmap($key):
        group_by(.[$key]) |
        map( { ($key): first[$key], entries: map(del(.[$key])) } );

.clientSettings   |= (groupmap("clientId")  | map(.entries |= from_entries)) |
.productSettings  |= (groupmap("productId") | map(.entries |= from_entries)) |
.customerSettings |= from_entries

これらの修正後の結果は次のようになります。

{
   "clientSettings": [
      {
         "clientId": 9876,
         "entries": { "lkmn": false }
      },
      {
         "clientId": 12345,
         "entries": { "abc": false, "def": false, "ghi": false }
      }
   ],
   "customerSettings": { "enableData": false, "minPriceValue": "1.0", "presentData": "AEGIS" },
   "osirisTest": {
      "7678": [ "K" ]
   },
   "productSettings": [
      {
         "entries": { "jkl": true, "mno": true },
         "productId": 11
      },
      {
         "entries": { "jkl": true, "mno": true },
         "productId": 12
      }
   ],
   "thothTest": {
      "5123": [ "L" ],
      "5431": [ "A", "L" ],
      "9876": [ "K" ]
   }
}

関連情報