【Python】階層構造になった辞書型データから一部を削除してみた

f:id:hira98:20191125205204p:plain

【結論】

  • 階層構造のデータは再帰関数で扱う(持論)
  • AnsibleやRobot Frameworkとは今後もビジネスパートーナーとして良好な関係を築いていきたい。

【目次】

はじめに

最近の業務内容です。

本題だけ知りたい人はスルーしてください😓

最近、「Python」と「robot framework」で実装された「WebAPIのテスト自動化ツール」を引き継ぎました。

このツールを引き継いで行う作業は次の2つです。

  1. ツールを使っての検証作業。

  2. 新たにWebAPIが追加された際にテストケースを作成する。

テストケースを作成するWebAPIは、「パラメーターを取得するAPI」と「パラメーター取得&設定を行うAPI」の大きく2つに分類されます。

「パラメーターを取得するAPI」は結果がJSON形式なので、どこを見て結果を判断するのかをツールの記載ルールに従って記載します。(例:起動時間を取得するAPIだと、時間が多少ずれるのは想定通りなのでスルーする。)

「パラメーター取得&設定を行うAPI」はGETで取得したデータをDELETEで削除し後にPOSTします。

これらの概要を把握して思ったことは、「GETしたデータを一部スルーする設定を書いたり、GETしたデータをそのままPUTするだけなんて、余裕じゃん😎」です。

しかし、その余裕はスグに打ち砕かれました😵

引き継いで初っ端に追加するテストケースがイレギュラーだったからです。

「パラメーター取得&設定を行うAPI」ですが、GETしたデーターの一部を除去してPOSTしないといけません。しかし、既存ロジックにそのようなテストケースはありませんでした。

既存のテストケースは全て「Robot Framework」で書かれています。

私も空気を読んで「Robot Framework」で実装しようとしたのですが「まぁーうまくいかない😱」。

Pythonだとこれでいけるんだけどなー😰」

「Robot Frameworkだとどう書けばいいんだ😭」

...ここで前の案件で「Pythonを捨ててAnsibleで突き進んだ結果みた地獄」がトラウマとして蘇ります🥶

下手に空気を読んで自分の意見を殺して突き進んだところで、待ち受けているのは底なしの後悔🤮

「Robot Framework」と「Python」どちらを選んでも過酷な道のりが待ち受けているのかもしれません。

ただ私は、どんな苦難も好きであれば乗り越えていけると信じています😤

僕が好きなのは「Robot Framework🤖」ではなく「Python🐍」。

Python🐍」お前と見る地獄なら大歓迎だぜ😘。

やりたいこと←ここからが本題

階層構造のjsonデータから、指定したデータだけを削除した結果を得たい。

  • 引数:
    • JSON形式のデータ
    • 削除したいJSONパス
      • 複数のJSONパスを指定する場合は"aaa","bbb"のようにカンマ区切りで指定する。
      • 階層が深いデータを削除したい場合は"aaa.aaa_1"のようにコンマ区切りで指定する。
  • 戻り値:
    • 引数で渡された「削除したいJSONパス」のデータを削除したJSON形式のデータ

実行結果サンプル

引数:JSON形式のデータ

{
    "aaa":{
    "aaa_1":"aaa_data1",
    "aaa_2":"aaa_data2", 
 },
  "bbb":"bbb_data",
  "ccc":{
    "ccc_1":{
        "ccc_2":"ccc_data",
    }
}

引数:削除したいJSONパス

["aaa.aaa_1", "bbb"]

戻り値:

{
    "aaa":{
    "aaa_2":"aaa_data2",
 },
  "ccc":{
    "ccc_1":{
        "ccc_2":"ccc_data",
    }
}

Pythonコード

import copy

def del_dic(data, keys):
    if len(keys) == 1:
        del data[keys[0]]
        return
    else:
        temp_key = keys.pop(0)
        temp_data = data.pop(temp_key)
        del_dic(temp_data, keys)
        data[temp_key] = temp_data

def del_dic_main(dic_data, del_list):
    work_data = copy.copy(dic_data)
    del_keys = copy.copy(del_list)
    del_keys.sort(reverse=True)
    for key in del_keys:
        key_list = key.split(".")
        if len(key_list) >= 1:
            del_dic(work_data, key_list)  
    return work_data

if __name__ == "__main__":
    test_data1 = {
        "aaa":{
            "aaa_1":"aaa_data1",
            "aaa_2":"aaa_data2",
        },
        "bbb":"bbb_data",
        "ccc":{
            "ccc_1":{
                "ccc_2":"ccc_data",
            }
        }
    }
    print(f"before: {test_data1}")
    test_data1 = del_dic_main(test_data1, ["aaa.aaa_2","bbb"])
    print(f"after: {test_data1}")

実行結果

before: {'aaa': {'aaa_1': 'aaa_data1', 'aaa_2': 'aaa_data2'}, 'bbb': 'bbb_data', 'ccc': {'ccc_1': {'ccc_2': 'ccc_data'}}}
after: {'ccc': {'ccc_1': {'ccc_2': 'ccc_data'}}, 'aaa': {'aaa_1': 'aaa_data1'}}

さいごに

ネットワークもいいけど、やっぱりこういうロジック考える方が好き。