ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Python 에서 JSON 사용하기
    Python 2023. 12. 10. 22:30

    Python 에서 JSON 사용하기

    얼마 전 Python 에서 JSON 을 다뤄야 하는 문제가 있었는데 문서는 안 읽고 매번 똑같은 커맨드 사용했더니 문제를 못 풀었어요.
    현타 오고 바보가 된 것 같아서 문서를 쭉 읽어보기로 해요.

    fp 를 다루는 dump, load 는 비슷하니, 더 자주 쓸 것 같은 dumps, loads 로 알아볼게요.


    json.dumps()

    def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
            allow_nan=True, cls=None, indent=None, separators=None,
            default=None, sort_keys=False, **kw)

     

    변환표

    • 파이썬 -> JSON 변환이 어떻게 되는지 알아봐요.
    파이썬 JSON
    dict 오브젝트(object)
    list, tuple 배열(array)
    str 문자열(string)
    int, float, int와 float에서 파생된 열거형 숫자(number)
    True true
    False false
    None null

     

    skipkeys=False

    • True 설정 시 기본형(str, int, float, bool, None)이 아닌 딕셔너리 키는 TypeError 를 발생시키지 않고 건너뛰어요.
    • 딕셔너리 키는 immutable 한 값이어야 하는데 그러면 위의 기본형을 제외하고는 tuple 밖에 생각이 안나네요.
    • dict key 값에 tuple 이 있을 때 사용하면 될 것 같아요.
    data = {"name": "kingsubin", "city": "busan", ('tuple_key', ): "soob"}
    
    json.dumps(data)
    # TypeError: keys must be str, int, float, bool or None, not tuple
    
    json.dumps(data, skipkeys=True)
    # {"name": "kingsubin", "city": "busan"}

     

    ensure_ascii=True

    • False 설정 시 비 아스키 문자들을 그대로 출력해 줘요.
    data = {"이름": "수빈", "city": "부산", "pc": "mac"}
    
    json.dumps(data)
    # {"\uc774\ub984": "\uc218\ube48", "city": "\ubd80\uc0b0", "pc": "mac"}
    
    json.dumps(data, ensure_ascii=False)
    # {"이름": "수빈", "city": "부산", "pc": "mac"}

     

    check_circular=True

    • 순환 참조를 검사할지 여부예요.
    • 이걸 False 로 설정하고 쓸 일이 있을지 잘 모르겠어요.
    data = {}
    data["self"] = data
    
    json.dumps(data)
    # ValueError: Circular reference detected
    
    json.dumps(data, check_circular=False)
    # RecursionError: maximum recursion depth exceeded while encoding a JSON object

     

    allow_nan=True

    data = {"nan": math.nan, "inf": float("inf"), "-inf": float("-inf")}  
    
    json.dumps(data)
    # {"nan": NaN, "inf": Infinity, "-inf": -Infinity}
    
    json.dumps(data, allow_nan=False)
    # ValueError: Out of range float values are not JSON compliant

     

    cls=None

    • type[JSONEncoder] 을 받아요.
    • 기본값으로는 json.JSONEncoder 를 사용하는데 특정한 객체를 JSON 으로 변환하는 방법을 정의하고 싶을 때 사용해요.
    • 많이 쓰이면서 serializable 하지 않은 set, datetime 같은 타입을 위해 커스텀인코더를 만들어 두고 쓰면 좋을 것 같은 생각이 들어요.
    data = {  
        "name": "kingsubin",  
        "cities": ["seoul", "busan"],  
        "nickname": "soob",  
        "timestamp": datetime.now(),  
    }
    
    class KingsubinEncoder(json.JSONEncoder):  
        def default(self, obj):  
            if isinstance(obj, set):  
                return list(obj)  
            elif isinstance(obj, datetime):  
                return obj.isoformat()
    
            return super().default(obj)
    
    json.dumps(data, cls=KingsubinEncoder)
    # {"name": "kingsubin", "cities": ["seoul", "busan"], "nickname": "soob", "timestamp": "2023-12-08T23:39:09.835149"}

     

    indent=None

    data = {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data)
    # {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data, indent=0)
    """
    {
    "name": "kingsubin",
    "city": "busan",
    "nickname": "soob"
    }
    """
    
    json.dumps(data, indent="umm")
    """
    {
    umm"name": "kingsubin",
    umm"city": "busan",
    umm"nickname": "soob"
    }
    """

     

    separators=None

    • tuple[str, str] 타입을 받아요. (item_separator, key_separator)
    • 기본값은 (', ', ': ') 이어서, 각 separator 뒤에 한 칸씩 공백이 있어요.
    • 공백을 줄이고 싶으면 (',', ':') 이렇게 뒤에 공백을 제거하면 되겠어요.
    data = {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data)
    # {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data, separators=(",", ":"))
    # {"name":"kingsubin","city":"busan","nickname":"soob"}
    
    json.dumps(data, separators=("item", "key"))
    # {"name"key"kingsubin"item"city"key"busan"item"nickname"key"soob"}

     

    default=None

    • 직렬화할 수 없는 객체에 대해 호출되는 함수예요.
    • 예시로 많이 쓰이는 직렬화 할 수 없는 datetime 객체를 다뤄봐요.
    data = {  
        "name": "kingsubin",  
        "city": "busan",  
        "nickname": "soob",  
        "timestamp": datetime.now(),  
    }
    
    def serialize_datetime(object):  
        if isinstance(object, datetime):  
            return object.isoformat()  
        return None
    
    json.dumps(data)
    # TypeError: Object of type datetime is not JSON serializable
    
    json.dumps(data, default=serialize_datetime)
    # {"name": "kingsubin", "city": "busan", "nickname": "soob", "timestamp": "2023-12-08T23:29:59.819621"}

     

    sort_keys=False

    data = {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data)
    # {"name": "kingsubin", "city": "busan", "nickname": "soob"}
    
    json.dumps(data, sort_keys=True)
    # {"city": "busan", "name": "kingsubin", "nickname": "soob"}
    
    data = {3: "kingsubin", 1: "busan", "nickname": "soob"}
    json.dumps(data, sort_keys=True)
    # TypeError: '<' not supported between instances of 'str' and 'int'

    json.loads()

    def loads(s, *, cls=None, object_hook=None, parse_float=None,
            parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

     

    변환표

    • JSON -> Python 변환이 어떻게 되는지 알아봐요.
    JSON 파이썬
    오브젝트(object) dict
    배열(array) list
    문자열(string) str
    숫자 (정수) int
    숫자 (실수) float
    true True
    false False
    null None

     

     

    cls=None

    • json.dumps() 에서 설명한 cls 와 같아요.

     

    object_hook=None

    • dict 대신 다른 객체를 만들 때 호출되는 함수를 지정하는 옵션이에요.
    • custom decoders 를 구현하는 데 사용할 수 있어요.
    • 예시로 json_string 에 datetime isoformat 이 존재하면 datetime 으로 바꿔주는 custom_hook 을 만들어봐요.
    json_string = '{"name": "kingsubin", "cities": ["seoul", "busan"], "nickname": "soob", "timestamp": "2023-12-08T23:54:18.839402"}'
    
    def custom_hook(dct):  
        for key, value in dct.items():  
            if isinstance(value, str):  
                try:  
                    dct[key] = datetime.fromisoformat(value)  
                except ValueError:  
                    pass
        return dct
    
    json.loads(json_string, object_hook=custom_object_hook)
    # {'name': 'kingsubin', 'cities': ['seoul', 'busan'], 'nickname': 'soob', 'timestamp': datetime.datetime(2023, 12, 8, 23, 54, 18, 839402)}

     

    parse_float=None

    • 디코딩될 모든 JSON float 에 대해서 호출돼요.
    json_string = '{"name": "subin", "age": 30, "city": "busan", weight: 64.123}'
    
    def custom_float_parser(weight):
        return rount(float(weight), 2)
    
    json.loads(json-string, parse_float=custom_float_parser)
    # {'name': 'subin', 'age': 30, 'city': 'busan', 'weight': 64.12}

     

    parse_int=None

    • 디코딩 될 모든 JSON int 에 대해서 호출돼요.
    • parse_float 과 같이 쓰면 될 것 같은데 적절히 쓰일만한 예시 생각이 안 나요.

     

    parse_constant=None

    • 디코딩 될 '-Infinity', 'Infinity', 'NaN' 에 대해서 호출돼요.
    • 자주 쓰이진 않을 것 같아요.
    json_string = '{"inf": Infinity, "neginf": -Infinity, "nan": NaN}'
    
    def custom_constant_parser(x):  
        if x == "Infinity":  
            return "hello"  
        elif x == "-Infinity":  
            return "hello2"  
        elif x == "NaN":  
            return "hello3"  
        return x  
    
    json.loads(json_string)
    # {'inf': inf, 'neginf': -inf, 'nan': nan}
    
    json.loads(json_string, parse_constant=custom_constant_parser)
    # {'inf': 'hello', 'neginf': 'hello2', 'nan': 'hello3'}

     

    object_pairs_hook=None

    • object_hook 과 유사하지만, dict 대신 (key, value) 이터레이터를 받아요.
    • object_hook 이 정의되어 있으면 object_pairs_hook 이 우선순위를 가져요.
    json_string = '{"NAME": "subin", "City": "busan"}'
    
    def custom_pairs_hook(pairs):  
        result = {}  
        for key, value in pairs:  
            result[key.lower()] = value  
        return result  
    
    json.loads(json_string)
    # {'NAME': 'subin', 'Age': 30, 'City': 'Busan'}
    
    json.loads(json_string, object_pairs_hook=custom_pairs_hook)
    # {'name': 'subin', 'age': 30, 'city': 'Busan'}

     


     

    한 번 봤으니 이제는 Python 으로 JSON 을 다룰 때 뚝딱뚝딱 거릴 수 있겠어요.

    날로 먹으려 하지 말고 문서를 보도록 해요...

    그럼 안녕


    참조:

     
     
     
     

     

    'Python' 카테고리의 다른 글

    pyenv 설정하기  (0) 2023.10.25
킹수빈닷컴