[python] JSON에서 유니 코드 대신 문자열 객체를 얻는 방법은 무엇입니까?

내가 사용하고 파이썬 2ASCII 인코딩 텍스트 파일 에서 JSON을 구문 분석하기 위해 를 있습니다.

json또는 로이 파일을로드하면 simplejson모든 문자열 값이 문자열 객체 대신 유니 코드 객체로 캐스팅됩니다. 문제는 문자열 객체 만 허용하는 일부 라이브러리에서 데이터를 사용해야한다는 것입니다. 나는 라이브러리를 변경할 수 없습니다 도를 업데이트합니다.

유니 코드가 아닌 문자열 객체를 얻을 수 있습니까?

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

최신 정보

이 질문은 오래 전에 파이썬 2 에 갇혀있을 때 물었 습니다 . 오늘날의 쉽고 깨끗한 솔루션 중 하나는 최신 버전의 Python (예 : Python 3 이상 )을 사용하는 것 입니다.



답변

솔루션 object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

사용법 예 :

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

어떻게 작동하며 왜 사용합니까?

마크 애 머리의 기능 은이 보다 짧고 명확합니다. 그렇다면 요점은 무엇입니까? 왜 그것들을 사용하고 싶습니까?

순수한 성능 . Mark의 답변은 먼저 유니 코드 문자열로 JSON 텍스트를 완전히 디코딩 한 다음 디코딩 된 전체 값을 반복하여 모든 문자열을 바이트 문자열로 변환합니다. 이것은 바람직하지 않은 몇 가지 영향을 미칩니다.

  • 전체 디코딩 구조의 복사본이 메모리에 생성됩니다.
  • JSON 객체가 실제로 깊게 중첩되어 있으면 (500 레벨 이상) Python의 최대 재귀 깊이에 도달합니다

이 답변 object_hookjson.load및 매개 변수를 사용하여 이러한 성능 문제를 모두 완화합니다 json.loads. 에서 워드 프로세서 :

object_hook(a dict) 객체 리터럴 디코딩 결과로 호출되는 선택적 함수입니다 . 대신에 object_hook의 반환 값이 사용됩니다 dict. 이 기능은 커스텀 디코더를 구현하는 데 사용할 수 있습니다

다른 딕셔너리에 많은 레벨이 중첩 된 딕셔너리 object_hook 는 디코딩 될 때 전달되기 때문에 , 그 시점에서 그 안에 문자열이나리스트를 바이트 화하고 나중에 깊은 재귀가 필요하지 않도록 할 수 있습니다.

Mark의 답변은 object_hook중첩 된 사전으로 되풀이되므로 그대로 사용하기에 적합하지 않습니다 . 우리는이 대답에 그 재귀 방지 ignore_dicts에 대한 매개 변수를 _byteify항상 그것에 전달되는, 제외 하면 object_hook이 새로운 전달 dictbyteify 할 수 있습니다. ignore_dicts플래그는 말한다_byteify 무시 dict이미 byteified 된 이후들.

마지막으로, 디코딩되는 JSON 텍스트가 최상위 레벨에 없는 경우를 처리 하거나 반환 된 결과에 대해 (with )를 구현 json_load_byteified하고 json_loads_byteified호출 합니다._byteifyignore_dicts=Truejson.loadjson.loadsdict


답변

여기에 좋은 대답이 있지만 키와 값을 유형 대신 유형 문자열 로 제공하기 때문에 PyYAML 을 사용하여 JSON 파일을 구문 분석했습니다 . JSON은 YAML의 하위 집합이기 때문에 잘 작동합니다.strunicode

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

노트

그래도주의해야 할 사항 :

  • 모든 항목이 ASCII로 인코딩 되어 있기 때문에 문자열 객체를 얻습니다 . 유니 코드로 인코딩 된 항목을 사용하면 유니 코드 개체 로 다시 가져올 수 있습니다 . 변환이 없습니다!

  • PyYAML의 safe_load기능을 사용해야합니다 (아마도 항상) . JSON 파일을로드하는 데 사용하는 경우 load어쨌든 함수 의 “추가 기능”이 필요하지 않습니다 .

  • 당신은 (그리고 사양의 1.2 버전에 대한 더 많은 지원이있는 YAML 파서하려면 매우 낮은 번호를 올바르게 구문 분석 ) 시도 Ruamel YAML을 : pip install ruamel.yaml그리고 import ruamel.yaml as yaml내 시험에 필요한 모든 I이었다.

변환

명시된 바와 같이, 전환이 없습니다! ASCII 값만 처리 할 수없고 대부분의 시간을 확신 할 수없는 경우 변환 함수를 사용하는 것이 좋습니다 .

나는 Mark Amery 의 제품을 몇 번 사용했지만 훌륭하게 작동하고 사용하기 쉽습니다. object_hook큰 파일의 성능이 향상 될 수 있으므로 비슷한 기능을 대신 사용할 수도 있습니다. 이에 대한 Mirec Miskuf 의 약간 더 관련된 답변을 참조하십시오 .


답변

json 모듈 함수가 유니 코드 문자열 대신 바이트 문자열을 반환하도록하는 기본 제공 옵션이 없습니다. 그러나이 짧고 간단한 재귀 함수는 디코딩 된 모든 JSON 객체를 유니 코드 문자열 사용에서 UTF-8로 인코딩 된 바이트 문자열로 변환합니다.

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

json.load또는 a에서 얻은 출력에서 ​​이것을 호출하십시오.json.loads 호출 호출하십시오.

몇 가지 메모 :

  • 이전 파이썬 2.6를 지원하거나, 교체 return {byteify(key): byteify(value) for key, value in input.iteritems()}return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]) 사전 함축 파이썬 2.7까지 지원되지 되었기 때문에.
  • 이 답변은 전체 디코딩 된 객체를 통해 반복되므로, object_hook또는 object_pairs_hook매개 변수 를 매우 신중하게 사용하여 피할 수있는 몇 가지 바람직하지 않은 성능 특성이 있습니다 . Mirec Miskuf의 대답 은 지금까지 이것을 올바르게 뽑아내는 유일한 방법입니다. 결과적으로 내 접근 방식보다 훨씬 더 복잡합니다.

답변

object_hook매개 변수를 사용하여 json.loads변환기를 전달할 수 있습니다 . 사실 후에 변환을 수행 할 필요가 없습니다. json모듈은 항상 통과 할 object_hook경우에만 dicts을, 당신은 중첩 된 dicts에 자신을 재귀 할 필요가 없습니다 그것은 반복적으로 중첩 dicts로 전달됩니다. 유니 코드 문자열을 Wells 쇼와 같은 숫자로 변환하지는 않을 것이라고 생각합니다. 유니 코드 문자열 인 경우 JSON 파일에서 문자열로 인용되었으므로 문자열이거나 파일이 잘못되었습니다.

또한, 나는 같은 일을 피하려고 것 str(val)A의 unicode객체를. value.encode(encoding)외부 라이브러리가 기대하는 바에 따라 유효한 인코딩을 사용해야합니다 .

예를 들어,

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)


답변

json은 문자열 객체와 유니 코드 객체 사이에 차이가 없기 때문입니다. 그들은 모두 자바 스크립트의 문자열입니다.

JSON이 유니 코드 객체를 반환하는 것이 옳다고 생각 합니다 . 사실, 자바 스크립트 문자열 은 실제로 unicode객체 (즉, JSON (javascript) 문자열은 모든 종류 의 유니 코드 문자를 저장할 수 있음 )이므로 객체를 덜 받아들이지 않으므로 JSON unicode에서 문자열을 변환 할 때 객체 를 만드는 것이 좋습니다 . 라이브러리는 원하는 인코딩을 추측해야하기 때문에 일반 문자열은 적합하지 않습니다.

unicode어디에서나 문자열 객체 를 사용하는 것이 좋습니다 . 따라서 가장 좋은 방법은 라이브러리를 업데이트하여 유니 코드 객체를 처리 할 수 ​​있도록하는 것입니다.

그러나 바이트 문자열을 정말로 원한다면 결과를 원하는 인코딩으로 인코딩하십시오.

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']


답변

쉬운 해결 방법이 있습니다.

TL; DR- ast.literal_eval()대신 사용하십시오 json.loads(). 모두 astjson표준 라이브러리에 있습니다.

‘완벽한’대답은 아니지만 계획에서 유니 코드를 완전히 무시하려는 경우에는 한 가지 결과를 얻을 수 있습니다. 파이썬 2.7에서

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

제공합니다 :

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

일부 객체가 실제로 유니 코드 문자열 인 경우 더 털이 있습니다. 완전한 대답은 빨리 털이 나옵니다.


답변

Mike Brennan의 대답 은 가깝지만 전체 구조를 다시 횡단 할 이유는 없습니다. object_hook_pairs(Python 2.7+) 매개 변수 를 사용하는 경우 :

object_pairs_hook정렬 된 쌍의 목록으로 디코딩 된 객체 리터럴의 결과와 함께 호출되는 선택적 함수입니다. 의 반환 값이 object_pairs_hook대신 사용됩니다 dict. 이 기능은 키와 값 쌍이 디코딩 collections.OrderedDict되는 순서 (예를 들어 삽입 순서를 기억할 것) 에 의존하는 사용자 정의 디코더를 구현하는 데 사용할 수 있습니다 . object_hook또한 정의 된 경우 object_pairs_hook우선 순위를 갖습니다.

이를 사용하면 각 JSON 객체를 사용할 수 있으므로 재귀 필요없이 디코딩을 수행 할 수 있습니다.

def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'

In [53]: json.load(open('test.json'))
Out[53]:
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]:
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

를 사용할 때 모든 객체가 후크에 전달되므로 후크를 재귀 적으로 호출 할 필요가 없습니다 object_pairs_hook. 리스트에 신경 쓸 필요는 있지만, 보시다시피,리스트 내의 객체는 올바르게 변환 될 것이며, 그것을하기 위해 재귀 할 필요는 없습니다.

편집 : 동료는 Python2.6에 object_hook_pairs. 파이썬 2.6을 아주 조금만 변경하여 사용할 수 있습니다. 위의 후크에서 다음을 변경하십시오.

for key, value in pairs:

for key, value in pairs.iteritems():

그런 다음 object_hook대신 사용하십시오 object_pairs_hook.

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]:
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

object_pairs_hookJSON 객체의 각 객체에 대해 하나의 적은 사전이 인스턴스화되는 결과를 사용하면 큰 문서를 구문 분석하는 경우 가치가있을 수 있습니다.