68757


# 実現したいこと

以下のような２つのJSONファイルを用いて、`menu.json`を書き換えようとしています。

`menu.json`における名前と`allergies.json`における名前が一致（部分一致した場合）、`allergies.json`の「素材」の項目を、想定出力のように`menu.json`に書き加えたいです。

menu.json

[code]

    {
        "メニュー":{
            "メインディッシュ":[
                {
                    "名前":"ステーキ",
                    "価格":1000
                },
                {
                    "名前":"焼き魚",
                    "価格":800
                }
            ],
    
            "サラダ":[
                {
                    "名前":"マカロニサラダ",
                    "価格":300
                },
                {
                    "名前":"シーザーサラダ　ハーフ",
                    "価格":500
                }
            ]
        }
    }
    
[/code]

allergies.json

[code]

    {
        "アレルギー":{
            "サラダ":[
                {
                    "素材":{
                        "乳製品":"チーズ",
                        "ナッツ":"くるみ"
                    },
                    "商品名":{
                        "名前1":"シーザーサラダ",
                        "名前2":"フレンチサラダ"
                    }
                }
    
            ]
        }
    }
    
[/code]

想定出力

[code]

    {
        "メニュー":{
            "メインディッシュ":[
                {
                    "名前":"ステーキ",
                    "価格":1000
                },
                {
                    "名前":"魚のソテー",
                    "価格":800
                }
            ],
    
            "サラダ":[
                {
                    "名前":"マカロニサラダ",
                    "価格":300
                },
                {
                    "名前":"シーザーサラダ　ハーフ",
                    "価格":500,
                    "素材":{
                        "乳製品":"チーズ",
                        "ナッツ":"くるみ"
                    }
                }
            ]
        }
    }
    
[/code]

# 発生している問題

以下まで現在できているのですが、`allergies.json`の"商品名"の値と`menu.json`の名前が一致したら、「素材」の項目を取ってくると言う分岐をどのように書けばいいのかわからず困っています。

現在の出力では以下のようにしか表示されず、実現したいことができていない状態です。

[code]

                {
                    "名前":"シーザーサラダ",
                    "価格":500
                }
    
[/code]

# コード

[jsonであるキーに対する値を取得し、値に応じて要素を追加したい](https://ja.stackoverflow.com/questions/68360/json%E3%81%A7%E3%81%82%E3%82%8B%E3%82%AD%E3%83%BC%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E5%80%A4%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97-%E5%80%A4%E3%81%AB%E5%BF%9C%E3%81%98%E3%81%A6%E8%A6%81%E7%B4%A0%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%81%84)という前の質問の回答を参考にさせていただいて、今回のコードを書きました。

[code]

    import json
    
    #ファイル読み出し
    file_path1 = 'menu.json'
    json_file1 = open(file_path1, 'r')
    json_object1 = json.load(json_file1)
    
    file_path2 = 'allergies.json'
    json_file2 = open(file_path2, 'r')
    json_object2 = json.load(json_file2)
    
    menus = json_object1["メニュー"]["サラダ"]
    allergies = json_object2["アレルギー"]["サラダ"]
    
    
    def get_allegies(allergies, name):
        print(allergies[0]["名前1"])
        if allergies[0]["名前1"] == name:
            return allergies[0]["素材"]
        return None
    
    def set_combination(menu, allergies, name_key, new_key):
        try:
            name = menu[name_key]
            allergies = get_allegies(allergies, name)
            menu[new_key] = allergies
    
        except KeyError:
            pass
    
    
    for menu  in menus:
        set_combination(menu, allergies, "名前", "素材")
    
    print(json.dumps(menu , indent=4))
    
    
    
[/code]

# 試したこと

普通の文字列であれば以下のようにして、含まれているかどうか判別できることはわかっています  
[参考記事](https://note.nkmk.me/python-str-compare/)

[code]

    print('bbb' in 'aaa-bbb-ccc')
    # True
    
[/code]

# 環境

python 3.7.4


CC BY-SA 4.0
2020-07-20T07:38:44.607
68750
2020-07-20T13:36:34.283
2020-07-20T10:01:07.820
question
python python3 json データ構造 ファイル入出力
JSON上の辞書の値で一致するものがあるかどうか判別したいが、どのように現在のコードを修正すれば良いかわからない


まず、`allergies.json`データの方を以下のように`素材`内容が同じでも別々のデータとして配列化します。

[code]

    {
        "アレルギー":{
            "サラダ":[
                {
                    "素材":{
                        "乳製品":"チーズ",
                        "ナッツ":"くるみ"
                    },
                    "商品名":{
                        "名前":"シーザーサラダ"
                    }
                },
                {
                    "素材":{
                        "乳製品":"チーズ",
                        "ナッツ":"くるみ"
                    },
                    "商品名":{
                        "名前":"フレンチサラダ"
                    }
                }
            ]
        }
    }
    
[/code]

そしてプログラムの方は以下記事の応用でjsonへのアクセス方法を単純化出来るようにします。  
[PythonでJSONデータを扱う](https://www.techscore.com/blog/2019/12/16/better-way-
handling-json-data-in-python/)

> **objectっぽくアクセスしたい**  
>  例えば、dictを継承して属性が有るかのように振る舞うクラスを定義する方法があります。
[code]

>     class ObjectLike(dict):
>         # __getattr__ は属性がなかった場合に実行される特殊メソッドで、dict.getを利用するようにする
>         __getattr__ = dict.get
>  
[/code]

>
> **jsonパッケージと一緒に使う**  
>  この ObjectLike をJSONの読み取りの際にも利用します。  
>  json.loads には
> object_hookという引数があり、JSONのObjectを処理する際のフックを仕込むことができるようになっています。dict
> を受け取って何らかの値を返す関数であれば良いので ObjectLike をそのまま使用することができます。
[code]

>     user = json.loads(json_data, object_hook=ObjectLike)
>  
[/code]

上記を応用して以下のように実装することが出来ます。  
Windows環境でやったのでencoding関連の指定を増やしています。

[code]

    import json
    
    class ObjectLike(dict):  #### 追加
        __getattr__ = dict.get  #### 追加
    
    file_path1 = 'menu.json'
    json_file1 = open(file_path1, 'r', encoding='utf-8')
    menus = json.load(json_file1, object_hook=ObjectLike)  #### 変更
    
    file_path2 = 'allergies.json'
    json_file2 = open(file_path2, 'r', encoding='utf-8')
    allergies = json.load(json_file2, object_hook=ObjectLike)  #### 変更
    
    #### アレルギー情報を主体にループする
    for alrgcategory in allergies.アレルギー:
        for alrgitem in allergies.アレルギー[alrgcategory]:
            itemname = alrgitem.商品名.名前
            
            #### メニューに該当項目があるか検索し、あれば情報を追記する
            for i, item in enumerate(menus.メニュー[alrgcategory]):
                if itemname in item.名前:
                    menus.メニュー[alrgcategory][i]['素材'] = alrgitem.素材
    
    print(json.dumps(menus, indent=4, ensure_ascii=False))
    
[/code]


CC BY-SA 4.0
2020-07-20T12:10:32.320
68755
2020-07-20T12:10:32.320
26370
68750
answer


入力する JSON ファイルを変更しない場合は以下の様になります。ここで、「`allergies.json` の"商品名"の値と `menu.json`
の名前が一致したら、『素材』の項目を取得する処理」は `products = ...` 以降の部分になります。

[code]

    import json
    from copy import deepcopy
    
    # menu
    with open('menu.json', 'r') as f:
      menu = json.load(f)
      # new dict for update
      updated_menu = deepcopy(menu)
      if 'メニュー' in menu:
        menu = menu['メニュー']
    
    # allergie
    with open('allergies.json', 'r') as f:
      allergies = json.load(f)
      if 'アレルギー' in allergies:
        allergies = allergies['アレルギー']
    
    # update
    for ak, av in allergies.items():
      if ak not in menu: continue
      for i, contents in enumerate(av):
        if '商品名' not in contents: continue
        products = [
          contents['商品名'][name] for name in contents['商品名']
          if name.startswith('名前')
        ]
        if not products: continue
        for j, v in enumerate(menu[ak]):
          if '名前' not in v: continue
          if any([p in v['名前'] for p in products]):
            updated_menu['メニュー'][ak][j]['素材'] = av[i]['素材']
    
    print(json.dumps(updated_menu, indent=4, ensure_ascii=False))
    
[/code]


CC BY-SA 4.0
2020-07-20T13:28:41.140
68757
2020-07-20T13:36:34.283
2020-07-20T13:36:34.283
68750
answer