json - JSON encoder and decoder
파이썬 코드 읽어보기 첫 번째 시리즈는 json
입니다.
import json
개발 중 흔하게 만나던 json
의 내부는 어떻게 되어 있는지 같이 확인해봅시다.
저는 cpython repository에서 코드를 확인해봤습니다. 파이썬 코드는 ./Lib/ 디렉토리 아래에서 확인할 수 있습니다.
cpython/Lib/json/
디렉토리안의 내용입니다.
__init__.py
decoder.py
encoder.py
scanner.py
tool.py
본 글에서는 decoder.py
와 scanner.py
를 확인해보겠습니다.
다음과 같은 메소드와 클래스에 대해 자세히 살펴보려합니다.
JSONDecoder
- cls
JSONDecodeError
- cls
_decode_uXXXX
- method
py_scanstring
- method
JSONObject
- method
JSONArray
- method
class JSONDecoder
부터 시작합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class JSONDecoder(object): def __init__(self, *, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None): self.object_hook = object_hook self.parse_float = parse_float or float self.parse_int = parse_int or int self.parse_constant = parse_constant or _CONSTANTS.__getitem__ self.strict = strict self.object_pairs_hook = object_pairs_hook self.parse_object = JSONObject self.parse_array = JSONArray self.parse_string = scanstring self.memo = {} self.scan_once = scanner.make_scanner(self)
def decode(self, s, _w=WHITESPACE.match): obj, end = self.raw_decode(s, idx=_w(s, 0).end()) end = _w(s, end).end() if end != len(s): raise JSONDecodeError("Extra data", s, end) return obj
def raw_decode(self, s, idx=0): try: obj, end = self.scan_once(s, idx) except StopIteration as err: raise JSONDecodeError("Expecting value", s, err.value) from None return obj, end
|
생성자의 각 파라미터에 대한 설명은 생략하겠습니다.
decode()
- JSON 문서를 포함하고 있는 스트링 객체를 파이썬 오브젝트로 반환합니다.
1 2 3 4 5 6 7
| >>> from json import JSONDecoder >>> decoder = JSONDecoder() >>> a = decoder.decode('{"a" : 1}') >>> a {'a': 1} >>> type(a) <class 'dict'>
|
raw_decode(self, s, idx=0)
- JSON 문서를 디코딩해서 파이썬 객체와 파라미터 s
가 끝나는 지점의 인덱스를 tuple
의 형태로 반환합니다.
return (object, len(s))
대략 이런 느낌입니다.
1 2
| >>> decoder.raw_decode('{"a": 123}') ({'a': 123}, 10)
|
1 2 3 4 5 6
| def raw_decode(self, s, idx=0): try: obj, end = self.scan_once(s, idx) except StopIteration as err: raise JSONDecodeError("Expecting value", s, err.value) from None return obj, end
|
어떤 경우에 StopIteration
이 발생하는지 확인해보겠습니다. self.scan_once(s, idx)
메소드는 생성자에서 scanner
로 부터 주입받은 것임을 알 수 있습니다. self.scan_once = scanner.make_scanner(self)
1 2 3
|
make_scanner = c_make_scanner or py_make_scanner
|
c
는 제가 잘 몰라서 py_make_scanner
중심으로 어떤 일들이 일어나고 있는지 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
def py_make_scanner(context): parse_object = context.parse_object parse_array = context.parse_array parse_string = context.parse_string match_number = NUMBER_RE.match strict = context.strict parse_float = context.parse_float parse_int = context.parse_int parse_constant = context.parse_constant object_hook = context.object_hook object_pairs_hook = context.object_pairs_hook memo = context.memo
def _scan_once(string, idx): try: nextchar = string[idx] except IndexError: raise StopIteration(idx)
if nextchar == '"': return parse_string(string, idx + 1, strict) elif nextchar == '{': return parse_object((string, idx + 1), strict, _scan_once, object_hook, object_pairs_hook, memo) elif nextchar == '[': return parse_array((string, idx + 1), _scan_once) elif nextchar == 'n' and string[idx:idx + 4] == 'null': return None, idx + 4 elif nextchar == 't' and string[idx:idx + 4] == 'true': return True, idx + 4 elif nextchar == 'f' and string[idx:idx + 5] == 'false': return False, idx + 5
m = match_number(string, idx) if m is not None: integer, frac, exp = m.groups() if frac or exp: res = parse_float(integer + (frac or '') + (exp or '')) else: res = parse_int(integer) return res, m.end() elif nextchar == 'N' and string[idx:idx + 3] == 'NaN': return parse_constant('NaN'), idx + 3 elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity': return parse_constant('Infinity'), idx + 8 elif nextchar == '-' and string[idx:idx + 9] == '-Infinity': return parse_constant('-Infinity'), idx + 9 else: raise StopIteration(idx)
def scan_once(string, idx): try: return _scan_once(string, idx) finally: memo.clear()
return scan_once
|
py_make_scanner.scan_once()
를 실행하면 내부에 정의된 메소드 scan_once()
가 _scan_once()
프라이빗 메소드를 부른 후 finally
구문을 통해 memo.clear()
를 실행합니다.
memo
변수는 memo = context.memo
를 통해 생성된 변수입니다. 그리고 context
는 JSONDecoder
클래스의 인스턴스입니다. 따라서 context.memo
는 dict
타입의 변수 입니다.
이상으로 decoder.py
와 scanner.py
를 간략하게 살펴 보았습니다.
다음으로는 json/tool.py
의 사용법과 코드를 보도록 하겠습니다.
감사합니다.