スタック・オーバーフロー Asked on September 1, 2021
以下のような2つのJSONファイルを用いて、menu.json
を書き換えようとしています。
menu.json
における名前とallergies.json
における名前が一致(部分一致した場合)、allergies.json
の「素材」の項目を、想定出力のようにmenu.json
に書き加えたいです。
menu.json
{
"メニュー":{
"メインディッシュ":[
{
"名前":"ステーキ",
"価格":1000
},
{
"名前":"焼き魚",
"価格":800
}
],
"サラダ":[
{
"名前":"マカロニサラダ",
"価格":300
},
{
"名前":"シーザーサラダ ハーフ",
"価格":500
}
]
}
}
allergies.json
{
"アレルギー":{
"サラダ":[
{
"素材":{
"乳製品":"チーズ",
"ナッツ":"くるみ"
},
"商品名":{
"名前1":"シーザーサラダ",
"名前2":"フレンチサラダ"
}
}
]
}
}
想定出力
{
"メニュー":{
"メインディッシュ":[
{
"名前":"ステーキ",
"価格":1000
},
{
"名前":"魚のソテー",
"価格":800
}
],
"サラダ":[
{
"名前":"マカロニサラダ",
"価格":300
},
{
"名前":"シーザーサラダ ハーフ",
"価格":500,
"素材":{
"乳製品":"チーズ",
"ナッツ":"くるみ"
}
}
]
}
}
以下まで現在できているのですが、allergies.json
の"商品名"の値とmenu.json
の名前が一致したら、「素材」の項目を取ってくると言う分岐をどのように書けばいいのかわからず困っています。
現在の出力では以下のようにしか表示されず、実現したいことができていない状態です。
{
"名前":"シーザーサラダ",
"価格":500
}
jsonであるキーに対する値を取得し、値に応じて要素を追加したいという前の質問の回答を参考にさせていただいて、今回のコードを書きました。
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))
普通の文字列であれば以下のようにして、含まれているかどうか判別できることはわかっています
参考記事
print('bbb' in 'aaa-bbb-ccc')
# True
python 3.7.4
入力する JSON ファイルを変更しない場合は以下の様になります。ここで、「allergies.json
の"商品名"の値と menu.json
の名前が一致したら、『素材』の項目を取得する処理」は products = ...
以降の部分になります。
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))
Correct answer by user39889 on September 1, 2021
まず、allergies.json
データの方を以下のように素材
内容が同じでも別々のデータとして配列化します。
{
"アレルギー":{
"サラダ":[
{
"素材":{
"乳製品":"チーズ",
"ナッツ":"くるみ"
},
"商品名":{
"名前":"シーザーサラダ"
}
},
{
"素材":{
"乳製品":"チーズ",
"ナッツ":"くるみ"
},
"商品名":{
"名前":"フレンチサラダ"
}
}
]
}
}
そしてプログラムの方は以下記事の応用でjsonへのアクセス方法を単純化出来るようにします。
PythonでJSONデータを扱う
objectっぽくアクセスしたい
例えば、dictを継承して属性が有るかのように振る舞うクラスを定義する方法があります。class ObjectLike(dict): # __getattr__ は属性がなかった場合に実行される特殊メソッドで、dict.getを利用するようにする __getattr__ = dict.get
jsonパッケージと一緒に使う
この ObjectLike をJSONの読み取りの際にも利用します。
json.loads には object_hookという引数があり、JSONのObjectを処理する際のフックを仕込むことができるようになっています。dict を受け取って何らかの値を返す関数であれば良いので ObjectLike をそのまま使用することができます。user = json.loads(json_data, object_hook=ObjectLike)
上記を応用して以下のように実装することが出来ます。
Windows環境でやったのでencoding関連の指定を増やしています。
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))
Answered by kunif on September 1, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP