@peco_2282

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

jsonデータをクラス化してデータを取り出したい

解決したいこと

タイトルの通りです

環境

py-3.10.4

該当するソースコード

data.json

[
  {
    "rendered_body": "...",
    "user": {
      "description": "discord.jsを触ろうか迷ってます。",
      "facebook_id": "",
      "followees_count": 1,
      "followers_count": 1,
      "github_login_name": "peco2282",
      "id": "peco_2282",
      "items_count": 11,
      "linkedin_id": "",
      "location": "",
      "name": "",
      "organization": "",
      "permanent_id": 1581903,
      "profile_image_url": "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581903/profile-images/1651145962",
      "team_only": "False",
      "twitter_screen_name": "peco_2282",
      "website_url": ""
    }
  }
]
item.py
class Item:
    def __init__(self, data: Dict[str, Any]):
        self.user: User = data.get("user")
user.py
class User:
    def __init__(self, data: Dict[str, Any]):
        self.description: Optional[str] = data.get("description")
        self.name: Optional[str] = data.get("name")

ここで、

main.py
with open("data.json", encoding="utf8", mode="r") as f:
    data = json.load(f)

if __name__ == "__main__":
    item = Item(data=data[0])
    print(item.user)
    print(item.user.description)

エラー

AttributeError: 'dict' object has no attribute 'description'

やはり出るなと思っていたのですが、javaとかのようにはいかないのでしょうか。
item.userは正しく取り出されています。

0 likes

4Answer

再帰的にインスタンス生成する必要がありますね。

import json
from typing import Dict, Any

def json_to_object(data: Any) -> Any:
    if isinstance(data, list):
        return [*map(json_to_object, data)]
    if isinstance(data, dict):
        return Item(data)
    return data


class Item:

    def __init__(self, data: Dict[str, Any]) -> None:
        for name, value in data.items():
            setattr(self, name, json_to_object(value))


if __name__ == "__main__":
    with open("data.json", encoding="utf8", mode="r") as f:
        data = json.load(f)

    item = json_to_object(data[0])
    print(item.user)
    print(item.user.description)
0Like

Comments

  1. @peco_2282

    Questioner

    ありがとうございます。
    この場合、予測変換に出てこないのですが、対処法はありますか?
  2. 予測変換がクラスオブジェクトのメンバーを列挙しているなら、出てきませんね。
    メンバーのないクラスを定義して、インスタンスにデータを代入しているだけなので。

namedtupleにする手もあります。

import json
from collections import namedtuple
from typing import Dict, Any


def json_to_object(data: Any) -> Any:
    if isinstance(data, list):
        return [*map(json_to_object, data)]
    if isinstance(data, dict):
        items = {name: json_to_object(item) for name, item in data.items()}
        return namedtuple('Item', items)(**items)
    return data


if __name__ == "__main__":
    with open("data.json", encoding="utf8", mode="r") as f:
        data = json.load(f)

    item = json_to_object(data[0])
    print(item.user)
    print(item.user.description)
0Like

コメントにあった予測変換がクラスオブジェクトを見ているのであれば、クラスを動的生成すれば表示されるかもしれません。

import json
from typing import Dict, Any


def json_to_object(name, data: Any) -> Any:
    if isinstance(data, list):
        return [*map(json_to_object, data)]
    if isinstance(data, dict):
        return type(name.capitalize(), (), {
            key: json_to_object(key, value) for key, value in data.items()
        })()
    return data


if __name__ == "__main__":
    with open("data.json", encoding="utf8", mode="r") as f:
        data = json.load(f)

    item = json_to_object('Item', data[0])
    print(item.user)
    print(item.user.description)
0Like
Item.py
class Item:
    def __init__(self, data: Dict[str, Any]):
        self.user: User = data.get("user")

Item.py
class Item:
    def __init__(self, data: Dict[str, Any]):
        self.user: User = User(data.get("user"))

としたらうまくいきました。
自己解決になってしまいましたが、namedtuple, setattr の知識がついたので感謝します。
大変ありがとうございました。

0Like

Your answer might help someone💌