[Python] dpath를 이용한 xpath 처럼 dict 에서 자료 구해오기 Develop Tip

RESTful API 를 이용한 FrontEnd 및 BackEnd의 JSON 데이터 연동 등은 이제
프로그램 개발에 있어 거의 그 자체가 표준처럼 인식되고 있습니다.
JSON 뿐만 아니라 동일한 1:1 변환이 가능한 YAML 또한 JSON 과 동일한
자료 표현 이외에 코멘트도 가능하고 읽기에 더 편하다는 이유 때문에
이제 설정파일 등으로 자주 사용됩니다.

다른 언어에서도 유사한 Key:Value 쌍을 표현하는 자료구조가 있기는 하지만,
파이썬의 dict 처럼 자유롭고 json과 거의 100% 동일하게 표현되는 자료구조는
좀처럼 찾기 쉽지 않습니다.

여기에다 파이썬의 dict는 MongoDB의 BSON으로 바로 저장된다는 것이
백엔드 로지과 MongoDB에 저장되는 것이 바로 되는 아주 환상적인 조합이 되는
것이지요.


따라서 사용자 측면에서 부터 개발까지,

YAML <=> JSON <=> Python dict <=> MongoDB BSON

이 모두 동일한 개념의 nested key, value 쌍으로 표현된다고 보면 됩니다.

이런 Nested 키/값 쌍은 또한 HTML 등의 tag 구조에서 데이터를 가져오는데
xpath 와 같은 방식으로 가져오면 좋겠다고 생각을 많이 했었습니다.

약 십여년 전부터 다음과 같은,

################################################################################
# noinspection RegExpSingleCharAlternation
def get_xpath(d, xpath, raise_exception=False, default_value=None):
    """XML 접근 방법으로 XPath가 있듯이 유사한 방법으로 파이썬의 dict를 XPath로 접속하는 함수

    :param dict d: 정보를 담고 있을 파이썬의 dict 객체
    :param str xpath: XPath 접근 문자열 (예, "/foo/lst[0]/@name2")
    :param bool raise_exception:
        - 만약 True 이고 접근이 불가능하면 ReferenceError 예외발생
        - 만약 False 이고 접근이 불가능하면 None 리턴
        - 디폴트는 False
    :param default_value: 만약 loop up 실패시 return 되는 디폴트 값
    :return: object dict에서 XPath 해당 값
    :raises ReferenceError: 만약 raise_exception 패러미터가 True 이고 접근이
        불가능하면 ReferenceError 예외발생 (디폴트 False)

    >>> ud = { u'spam': u'eggs', u'foo': True, u'foo': { u'baz': 97,
    ... 'lst':[{'@name':'aaa'},{'@name':'bbb'},{'@name':'ccc'},]}}
    >>> print ud[u'foo']['baz'] # dict 를 바로 접근함
    97
    >>> get_xpath(ud,'/foo/baz') # XPath 형식으로 가져옴
    97
    """
    if not isinstance(d, dict):
        if raise_exception:
            raise ReferenceError('vvjson.get_xpath: Invalid type <%s> must dict'
                                 % type(d))
        return default_value
    try:
        if xpath.strip() == '/':
            return d
        xeles = re.split(r'/|\[', xpath.strip('/'))
        for xele in xeles:
            xele = xele.strip()
            if xele[-1] == ']':
                xndx = int(xele[:-1])
                if not isinstance(d, (list, tuple)):
                    if xndx == 0:
                        continue  # regards /a[0] == /a
                    if raise_exception:
                        raise ReferenceError('vvjson.get_xpath: '
                                             'Invalid Index <%s>' % xndx)
                    return default_value
                d = d[xndx]
            else:
                d = d[xele]
        return d
    except KeyError as e:
        if raise_exception:
            raise ReferenceError('vvjson.get_xpath: Invalid Key <%s>'
                                 % str(e))
    except Exception as e:
        if raise_exception:
            raise ReferenceError('vvjson.get_xpath: Error <%s>' % str(e))
        return default_value


라이브러리를 만들어 사용하고 있었습니다만,
좀 더 쉽게 이용할 수 있는 모듈이 있어 소개해 봅니다.

해당페이지 를 참조하셔요.

pip install dpath 라고 해서 설치합니다.

d = {
  'a': {
    'b': {
      'c': [ 1, 2, '3', 'd', {'e': 5}]
    }
  }
}

와 같이 d 라는 변수에 dict 자료가 저장되어 있다고 하면,

import dpath.util

r = dpath.util.get(d, '/a/b/c/4/e')
와 같이 값을 찾으면 5 라는 결과가 나옵니다.

리스트에서는 0색인으로 찾아오고 나머지는 키에 해당되는 값을 주면 됩니다.

만약 존재하지 않는 키라면 defualt 패러미터를 넣어 값을 지정할 수 있습니다.

r = dpath.util.get(d, '/f/g/h', default=None)
라고 하면 None이 옵니다. 이 디폴트 값을 주지 않으면 예외가 발생합니다.

dpath.util.set 은 반대로 dict 에 값을 넣는 방법을 제공합니다.
그 밖에도 new, merge 등의 함수가 존재합니다.

더 자세한 것은 위에 페이지를 참조합니다.


어느 분께는 도움이 되셨기를..


덧글

댓글 입력 영역

구글애드텍스트