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


276

내가 사용하고 파이썬 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 이상 )을 사용하는 것 입니다.


1
Python3에서는 아무런 문제가 없습니다. new_list의 항목 유형은 다음과 같습니다.str
GoingMyWay

1
Python 3k는 '최신 Python 버전'이 아니며 대체 지점입니다.
user2589273

11
2017 년 12 월에 그러한 의견을 보는 것이 이상합니다-Python 2는 더 이상 사용되지 않으며 2020 년 1 월 1 일 이후 2 년 미만인 유지 관리는 수행되지 않습니다. pythonclock.org
Zaar Hai

1
@ZaarHai 많은 사람들이 자신의 의지에 반하여 파이썬 2에 갇혀 있습니다. 자동화 및 스크립팅을 위해 자체 Python 버전을 포함하는 많은 응용 프로그램이 있으므로 사람들은 공급 업체가 업데이트 될 때까지 사용해야합니다 (Nake Houdii, Nuke.)
Geordie

1
@Gediedie 확실히 알고 이해합니다. 내 의견은 용어에 관한 것이었다-파이썬은 "대체적인 지점"이 아니라, 불행한 대안이 부족하다.
Zaar Hai

답변:


101

솔루션 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


1
접근 방식에 +1; 처음 읽을 때 실제로 이해하지는 못했지만 Travis Jensen의 답변에 비추어 다시 읽을 때 이해했습니다. 나는 그것이 어떻게 작동하고 내 대답에 대한 이점이 무엇인지 명확히하기 위해 상당히 적극적으로 편집했습니다. 코드의 핵심 아이디어는 그대로 유지되지만 다른 모든 것을 수정했습니다. 당신이 이것에 반대한다면 내 편집을 롤백하십시오-그것은 당신의 대답입니다!
Mark Amery

문제 없습니다 Mark, 많은 감사합니다. 편집 내용이 마음에 듭니다. 원본보다 설명이 훨씬 쉽습니다. 언젠가 더 간결한 대답을하는 법을 배우겠습니다.
Mirec Miskuf

2
이것은 훌륭한 솔루션입니다. 효율적이고 우아합니다. : 파이썬 <2.7의 영역에 갇혀 있다면 내가 그러나, 당신은 라인을 교체해야합니다 return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }return dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())그것을위한 작업에.
Richard Dunn

나는 당신이 재귀 깊이 문제에 대해 틀렸다고 생각합니다. 당신과 함께, 나는 990까지 올라갈 수 있습니다 json_loads_byteified('[' * 990 + ']' * 990). 991에서는 충돌이 발생합니다. Mark는 여전히 991에서 작동합니다 byteify(json.loads('[' * 991 + ']' * 991)). 992에서 충돌합니다. 최소한이 테스트에서 Mark의 말은 당신이 말한 것과 반대로 더 깊어 질 수 있습니다.
Stefan Pochmann

@MarkAmery 위의 의견에 대해 어떻게 생각하십니까? (방금 편집 내역에서 실제로 해당 주장을 추가 한 사람이라는 것을 알았습니다).
Stefan Pochmann

180

여기에 좋은 대답이 있지만 키와 값을 유형 대신 유형 문자열 로 제공하기 때문에 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 의 약간 더 관련된 답변을 참조하십시오 .


8
이 답변을 사용하기로 결정한 경우 약간의주의를 기울이십시오. Brutus의 경우에는 완벽하게 작동하지만 데이터에 ASCII로 인코딩 가능한 문자 만 포함되어 있다는 것을 알고 있기 때문입니다. 보증하지 않으면이 답변이 효과가 없습니다. 예를 들어, yaml.load(json.dumps([u'a', u'£', u'É']))파이썬 쉘에서 실행 해보고 ['a', u'\xa3', u'\xc9']( unicode문자열 이 포함 된 ) 돌아 오는 것을 관찰하십시오 . 데이터에 ASCII 문자 집합의 문자 만 포함되어 있는지 확신 할 수없는 경우 다른 방법을 사용해야합니다 (내 대답을 권장합니다).
Mark Amery

1
YAML은 또한 [u'a', u'b']주의해서 사용 합니다.
Carlos Calla

1
이것은 좋지만 숫자가 낮 으면 작동하지 않습니다. 여기를보십시오 : stackoverflow.com/questions/30458977/…
Oren

@Oren : 이것은 YAML 사양 에서 오류가 아니라 PyYAML 파서에서 오류입니다 . Ruamel 작품 의 YAML 파서 .
Brutus

[ "a", "b"]와 같은 출력을 원하지 않음 [ 'a', 'b'] @Brutus
user60679

141

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의 대답 은 지금까지 이것을 올바르게 뽑아내는 유일한 방법입니다. 결과적으로 내 접근 방식보다 훨씬 더 복잡합니다.

1
나는 이것을 좋아한다-그것은 무시하지 않는다-사람들이 "문자열"과 "아스키"를 말할 때 그들은 이론적으로 유니 코드 문자가 아니라 바이트를 원한다는 것을 순진하게 의미한다는 것을 인식하고있다. (그리고 그들은 다른 쪽 끝에 파운드 기호를 원하기 때문에 ascii가 아닙니다)
Danny Staple

json이 튜플을 만들지 않는다는 것을 알고 있으므로 tuple에 대한 예외도 추가해야합니다.
y.petremann

이것은 끔찍하게 비효율적이므로 필요하지 않은 노드를 재귀 적으로 순회해야합니다. json 모듈을 사용하면 훨씬 효율적으로 수행 할 수 있습니다. 아래의 사용법 object_hook은 실제로 이것보다 훨씬 나쁘지만을 사용 object_pairs_hook하면 문자열을 포함하지 않는 노드를 재귀하거나 다시 방문하지 않아도 되는 합리적인 방법 을 얻을 수 있습니다.
트래비스 젠슨

1
@TravisJensen 재미있는. 이 object_pairs_hook방법은 아마도이 방법보다 이해하기가 매우 어려울 수 있습니다 (매개 변수의 작동 방식과 목록과 dicts에 다른 처리가 필요한 이유를 이해해야 함). 성능 이점은 대부분의 사람들에게 중요하지 않습니다 ...하지만 기대합니다 특히 비정상적으로 중첩 된 JSON 객체를 다루는 모든 사람에게 존재합니다.
Mark Amery

plus1 가장 간결한 답변입니다. PyYAML 외에도 설치가 어려움입니다. 유일하게 더 나은 방법은 변환을 마이크로 스트림하여 4X 메모리를 사용하지 않는 것입니다.
personal_cloud

74

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)

3
객체 s가 JSON Object(키와 값을 쉼표로 구분하고 중괄호로 묶은 키와 값을 구분하는 ':'문자로 정렬되지 않은 키 : 값 쌍의 컬렉션) 이면 괜찮습니다 . JSON Array. 따라서 JSON Array과 같은 것이 주어지면 ["a", "b"]결과는 여전히입니다 [u'a', u'b']. 현재 사용 가능한 다른 사용자 정의 후크 유형 매개 변수 json.loads()중 어느 것도 작업을 수행 할 수 없습니다.
martineau

2
언급했듯이 json모듈은 재귀 적으로 중첩 된 dicts를 전달 하므로 두 함수에서 모듈 을 확인할 필요가 없으므로 elif이를 확인 하는 두 절을 제거해야합니다.
martineau

1
밑줄로 시작하는 함수 이름은 import 문에 특별한 의미가 있습니다. 이러한 함수를 Utility.py라는 파일에 넣고 다른 파일에서는 do from Utility import *를 사용하면 해당 밑줄 때문에 함수가 표시 되지 않습니다 .
M Katz

1
이것은 정말 나쁜 생각입니다. object_hook파싱 ​​된 모든 json 객체에 대해 호출됩니다. 따라서 주어진 내용을 되풀이하면 이미 "바이트 화 된"항목을 다시 "바이트 화"합니다. 객체의 크기에 따라 성능이 기하학적으로 커질 것입니다. 나는 그 문제 를 사용 하고 겪지 않는 대답을 여기에 포함시켰다 object_pairs_hook.
트래비스 젠슨

38

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']

고마워 nosklo, 그것이 내가 먼저 한 일입니다. 그러나 내가 말했듯이, 내가 사용한 실제 데이터는 상당히 중첩되어있어서 약간의 오버 헤드를 조용히 도입했습니다. 나는 여전히 자동 솔루션을 찾고 있습니다 ... 사람들이 유니 코드 대신 문자열 객체를 반환하는 simplejson에 대해 불평하는 버그 보고서가 적어도 하나 있습니다.
Brutus

1
@ Brutus : json이 유니 코드 객체를 반환하는 것이 옳다고 생각합니다. 실제로 자바 스크립트 문자열은 실제로 유니 코드 객체이기 때문에 더 적은 것을 받아들이지 않을 것입니다. 내 말은 json (자바 스크립트) 문자열은 모든 종류의 유니 코드 문자를 저장할 수 있으므로 json에서 번역 할 때 유니 코드 객체를 만드는 것이 좋습니다. 대신 실제로 라이브러리를 수정해야합니다.
nosklo 2016 년

16

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

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'}

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


11
python에서는 유효하지 않으며 실패 할 수 있으므로 json nulltrue, 또는 false값을 포함하지 않는 것이 literal_eval()좋습니다.
ʇsәɹoɈ

3
@ ʇsәɹoɈ 또한 JSON \/에 문자열 안에 이스케이프 된 solidus ( ) 또는 유니 코드 이스케이프 시퀀스 (와 같은 "\u0061"다른 작성 방법 )가 포함되어 있지 않아야합니다 "a". 파이썬의 리터럴 구문은 여러 가지 방법으로 JSON과 호환되지 않으며, 버리지 않을 스크립트에 대해서는이 답변을 신뢰하지 않습니다.
Mark Amery

사람들이 문자열이 실제로 유니 코드라면이 대답은 실패하지만 그 경우 우리는 어쨌든 문자열로 캐스팅 할 수 없을 것입니다. 작동하고 예외를 던질 때만 작동하는 답변에 +1
Stefan Sullivan

가능한 경우 json데이터를 덤프하는 데 사용하지 말고 print파이썬을 실행 하는 경우 에만 사용 하십시오. 그런 다음 ast.literal_eval작동
장 - 프랑수아 파브르

11

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


1
이것은 깔끔하고 녹색 체크 표시를받을 가치가 있습니다 (Brutus가 더 나은 답변을 얻었을 때 이미 자유 롭게 통과했습니다). 그러나 ... deunicodify_hook이 답변 에 나타난 목록에서 실제로 목록을 올바르게 처리하지 않는 이유 는 무엇입니까? 현재 deunicodify_hook목록을 반복하지 않고 구현 하여 문자열과 목록 내의 문자열과 목록을 고유 화하지 않으므로 표시 하는 출력이 후크가 실제로 생성하는 출력과 일치 하지 않습니다 . 그것을 고치면이 대답은 내 것보다 우수합니다.
Mark Amery

사소한 : 또한 여기에서 사용하는 것이 아니라 일반적인 CPython 인터프리터로 함수를 시연하는 것이 좋습니다 (IronPython이라고 생각합니다)? CPython 인터프리터는 대부분의 Python 사용자에게 친숙하며 제 생각에는 더 예쁘습니다.
Mark Amery

이것은 나를 위해 작동하지 않지만 내가하고있는 일의 기발한 일이라고 확신합니다 ... 더 큰 json 문서에서 파일로 하나의 목록을 저장하고 있습니다. 이 object_pairs_hook의 유무에 관계없이 모든 항목이 유니 코드로 나타납니다. 꿰매다.
rsaw jan

1
@rsaw 좋은 지적! 객체에object_pairs_hook 대해서만 호출 되기 때문에 JSON 텍스트에 최상위 레벨의 문자열 목록이 있으면이 솔루션이 실패합니다. 에서 반환 된 것에서 일부 함수를 호출하지 않고이를 해결할 수있는 방법이 없습니다 . 어떤 고리도 당신이 모든 문자열을 다룰 수 있다고 보장 할 수는 없습니다. 나는 이것이 후크를 사용하는 것보다 내 솔루션을 계속 추천 할만 큼 큰 결함이라고 생각합니다. json.loadjson.load
Mark Amery

-1 Mirec Miskuf가 이미 Mike Brennan의 접근 방식 (동일한 사전을 여러 번 재 바이트 화) 또는이 방법 (중첩 목록 또는 최상위 목록을 바이트 화하지 못함)의 단점이없는 객체 후크 응답을 게시 했음을 깨달았 기 때문에 또는 문자열). 왜 그의 답변이 별다른 주목을받지 못한 채 잘 풀리지 않는지 잘 모르겠습니다.
Mark Amery

9

simplejson 라이브러리 내에서 자동으로이를 수행 할 수있는 방법이 없습니다.

simplejson의 스캐너와 디코더는 유니 코드 텍스트를 생성하도록 설계되었습니다. 이를 위해 라이브러리는 (사용 c_scanstring가능한 경우, 속도로) 또는 py_scanstringC 버전을 사용할 수없는 경우 라는 함수를 사용 합니다. 이 scanstring함수는 simplejson이 텍스트를 포함 할 수있는 구조를 디코딩하기 위해 갖는 거의 모든 루틴에 의해 여러 번 호출됩니다. scanstringsimplejson.decoder 또는 서브 클래스 의 값을 monkeypatch하고 JSONDecoder텍스트를 포함 할 수있는 모든 것을 구현해야합니다.

그러나 simplejson이 유니 코드를 출력하는 이유는 json 스펙 에서 "문자열은 0 개 이상의 유니 코드 문자의 집합"이라고 명시 하고 있기 때문입니다. 유니 코드에 대한 지원은 형식 자체의 일부로 가정됩니다. Simplejson의 scanstring구현은 유니 코드 이스케이프 (잘못된 형식의 멀티 바이트 문자셋 표현에 대한 오류 검사조차도)를 스캔하고 해석하기까지하므로 값을 안정적으로 반환 할 수있는 유일한 방법은 유니 코드입니다.

을 필요로하는 오래된 라이브러리가있는 경우 str구문 분석 후 중첩 된 데이터 구조를 힘들게 검색하거나 (명백하게 말하고 싶지 않은 것이 있습니다 ... 죄송합니다.) 어쩌면 라이브러리를 보다 세밀한 수준에서 입력 매개 변수를 마사지 할 수있는 정면. 데이터 구조가 실제로 깊이 중첩되어있는 경우 두 번째 방법은 첫 번째 방법보다 관리하기 쉽습니다.


4

Mark (Amery)는 올바르게 메모합니다 : json 덤프에서 PyYaml 의 deserializer를 사용하면 ASCII 만있는 경우에만 작동합니다. 적어도 상자 밖으로.

PyYaml 접근법에 대한 두 가지 빠른 의견 :

  1. 필드의 데이터에 yaml.load를 사용하지 마십시오 . 구조 내에 숨겨진 임의의 코드를 실행하는 yaml의 기능 (!)입니다.

  2. 다음을 통해 ASCII가 아닌 경우에도 작동 시킬있습니다 .

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)

그러나 성능은 Mark Amery의 답변과 비교할 수 없습니다.

깊게 중첩 된 샘플 dicts를 두 가지 방법으로 던지면 이것을 얻습니다 (dt [j] = json.loads (json.dumps (m))의 시간 델타 사용).

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

직렬화 트리를 걸어 완전히 포함 그래서 잘 JSON의 C 기반 구현의 크기의 순서 내에서 인코딩. 깊이 중첩 된 구조의 yaml로드보다 훨씬 빠르고 강력합니다. yaml.load를 보면 보안 오류가 적습니다.

=> C 전용 변환기에 대한 포인터에 감사하지만 byteify 함수 가 기본 응답이어야합니다.

이것은 json 구조가 사용자 입력을 포함하는 필드에서 온 경우 특히 그렇습니다. 따라서 원하는 내부 데이터 구조 ( '유니 코드 샌드위치'또는 바이트 문자열 만 해당)에 독립적으로 구조를 가로 질러 가야 할 수도 있습니다 .

왜?

유니 코드 정규화 . 알지 못하는 경우 : 진통제를 복용하고 이것을 읽으십시오 .

따라서 바이트 화 재귀를 사용하면 하나의 돌로 두 마리의 새를 죽입니다.

  1. 중첩 된 json 덤프에서 바이트 문자열을 가져옵니다.
  2. 저장소에서 물건을 찾을 수 있도록 사용자 입력 값을 정규화하십시오.

내 테스트에서 input.encode ( 'utf-8')을 unicodedata.normalize ( 'NFC', input) .encode ( 'utf-8')로 바꾸는 것이 NFC가없는 것보다 훨씬 빠르다는 것이 밝혀졌습니다. 그것은 내가 생각하는 샘플 데이터에 크게 의존합니다.


3

잡았다는 점이다 simplejson그리고 json그들이 유니 코드를 다루는 방식으로 적어도 두 개의 서로 다른 모듈입니다. 당신은이 json평 2.6+에서,이는 반면, 값을 유니 코드로 제공 simplejson반환 문자열 객체. 환경에서 easy_install-ing simplejson을 시도하고 작동하는지 확인하십시오. 그것은 나를 위해했다.


2

덤프 및로드에 json 대신 pickle을 사용하십시오.

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

생성되는 출력은 다음과 같습니다 (문자열 및 정수가 올바르게 처리됨).

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}

1
(같은 추가 패키지를 필요로하지 않는 솔루션 +1 YAML을 ). 그러나 때로는 원래의 경우와 마찬가지로 JSON에 데이터가 있어야하므로 pickle 이 항상 최선의 방법은 아닙니다. 게다가, 당신은 safe_loadYAML에 있습니다, pickle 과 비슷한 것이 있는지 모르겠습니다 .
Brutus

1

그래서 나는 같은 문제에 부딪쳤다. 첫 번째 Google 결과가 무엇인지 맞춰보세요.

모든 데이터를 PyGTK에 전달해야하기 때문에 유니 코드 문자열은 나에게별로 유용하지 않습니다. 그래서 또 다른 재귀 변환 방법이 있습니다. 실제로 형식 안전 JSON 변환에도 필요합니다. json.dump ()는 Python 객체와 같은 리터럴이 아닌 모든 항목에서 구제됩니다. dict 인덱스를 변환하지 않습니다.

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj

여기에 나타날 수있는 유일한 문제는 유니 코드에서 변환 된 사전의 키가 필요한 경우입니다. 이 구현은 값을 변환하지만 유니 코드 키를 유지 관리합니다. 'newobj'를 작성하는 경우 newobj [str (i)] = ...을 사용하고 완료되면 obj = newobj를 지정하면 키도 변환됩니다.
Neal Stublen

이것은 키를 변환함으로써 이해력이 더 좋거나 더 좋을 수 있습니다. 또한 단일성입니다. 둘 다 객체를 제자리에서 변경하고 (사전의 경우) 현재 값을 변경하거나 새 객체를 반환하는 Python의 내장 컬렉션 메소드와 일치하지 않는 새 값을 반환하지만 둘다는 아닙니다.
Mark Amery

1

나는 JSON dict을 문자열로 가지고있었습니다. 키와 값은 다음 예와 같이 유니 코드 객체였습니다.

myStringDict = "{u'key':u'value'}"

를 사용하여 byteify문자열을 dict객체 로 변환하여 위에서 제안한 함수를 사용할 수 있습니다 ast.literal_eval(myStringDict).


제시 한 예는 JSON의 예가 아닙니다. {u'key':u'value'}JSON이 아닙니다.
Mark Amery

2
나는 그것이 JSON이 아니라는 것을 완벽하게 알고 있습니다. 그것이 내 파이썬 스크립트의 외부 소스에서 구문 분석 된 방법입니다. 다음 예제에서와 같이 JSON 인 경우 솔루션으로 표시된 byteify 함수가 필요하지 않습니다. { "firstName": "John", "lastName": "Doe"}. 투표하기 전에 답을 읽으면 좋을 것입니다. 감사.
narko

1

후크를 사용하여 Python2 & 3 지원 ( https://stackoverflow.com/a/33571117/558397 )

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) 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):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

보고:

 {'three': '', 'key': 'value', 'one': 'two'}

0

이 게임은 늦었지만이 재귀 캐스터를 만들었습니다. 그것은 내 요구에 효과적이며 상대적으로 완벽하다고 생각합니다. 도움이 될 수 있습니다.

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

다음과 같이 JSON 객체를 전달하십시오.

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

나는 그것을 클래스의 개인 멤버로 가지고 있지만, 당신은 당신이 적합하다고 생각하는 방법을 재사용 할 수 있습니다.


JSON을 구문 분석하고 결과 매핑을 ** kwargs로 함수에 전달하려고하는 문제가 발생했습니다. 함수 매개 변수 이름은 유니 코드가 될 수없는 것처럼 보이므로 _parseJSON 함수가 좋습니다. 더 쉬운 방법이 있으면 누군가 알려주세요.
Neal Stublen

1
이 코드에는 문제가 있습니다. List 부분에서 재귀 호출을 수행하면 목록의 요소 자체가 사전이 아닌 경우 실패합니다.
I8214

@ I82Much에 의해 설명 된 버그 외에도, 이것은 실제로 이름이 잘못되었습니다 (실제로 JSON을 구문 분석하지는 않습니다. json.loads호출이 먼저 필요합니다). 붙여 넣습니다.
Mark Amery

0

json 객체 자체가 배열 (사용 사례) 인 경우를 처리하기 위해 Wells의 _parse_json ()을 다시 작성했습니다.

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj

0

다음은 C로 작성된 재귀 인코더입니다. https://github.com/axiros/nested_encode

"평균"구조의 성능 오버 헤드는 json.loads와 비교하여 약 10 %입니다.

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

이 테스트 구조를 사용하여 :

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)

0

Python 3.6을 사용하면 때때로이 문제가 계속 발생합니다. 예를 들어 REST API에서 응답을 받고 응답 텍스트를 JSON으로로드 할 때 여전히 유니 코드 문자열이 표시됩니다. json.dumps ()를 사용하여 간단한 솔루션을 찾았습니다.

response_message = json.loads(json.dumps(response.text))
print(response_message)

-1

나는이 문제에 부딪 쳤고 JSON을 다루어야 만 유니 코드 키를 문자열로 변환하는 작은 루프를 생각해 냈습니다. ( simplejsonGAE에서는 문자열 키를 반환하지 않습니다.)

obj JSON에서 디코딩 된 객체입니다.

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargs내가 GAE 응용 프로그램의 생성자에게 전달하는 것입니다 ( unicode키 를 좋아하지 않습니다 **kwargs)

Wells의 솔루션만큼 강력하지는 않지만 훨씬 작습니다.


-1

나는의 코드 적응 한 대답마크 Amery을 특히 제거하기 위해, isinstance오리 타이핑의 전문가를위한.

인코딩은 수동으로 수행 ensure_ascii되며 비활성화됩니다. 파이썬 문서 json.dump

ensure_ascii가 True (기본값)이면 출력의 모든 비 ASCII 문자는 \ uXXXX 시퀀스로 이스케이프됩니다.

면책 조항 : doctest에서 헝가리어를 사용했습니다. 헝가리어 관련 문자 인코딩은 다음과 같습니다 cp852. 예 : IBM / OEM 인코딩. DOS에서 (때로는 ascii 라고도 함 ), 예를 들어 코드 페이지 설정 에 따라 다릅니다 cp1250. Windows에서는 ( 로케일 설정에 따라 ansi로 지칭 됨 ) iso-8859-2, 때로는 http 서버에서 사용됩니다. 테스트 텍스트 Tüskéshátú kígyóbűvölőKoltai László (네이티브 개인 이름 양식)에 속하며 wikipedia의 것 입니다.

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

내가 강조하고자도 좋겠 대답자렛 하디를 하는 참조합니다 JSON 사양 , 인용 :

문자열은 0 개 이상의 유니 코드 문자 모음입니다.

내 유스 케이스에는 json 파일이 있습니다. 그들은 utf-8인코딩 된 파일입니다.ensure_ascii올바르게 이스케이프되었지만 읽을 수없는 json 파일이 생성되므로 Mark Amery의 답변을 필요에 맞게 조정했습니다.

doctest는 특별히 사려 깊지는 않지만 누군가에게 유용 할 것으로 기대하면서 코드를 공유합니다.


오리 타자를 사용하면 어떤 이점이 있는지 잘 모르겠습니다. 우리는 반환 된 컬렉션 json.loads이 메소드와 마술 메소드를 구현하는 일부 사용자 정의 또는 라이브러리 정의 유형이 아닌 목록 또는 딕트가 될 것이라는 것을 알고 있습니다. 왜 isinstance검사를 하지 않습니까? 쉽게는의 존재를 확인하는 것보다 이해되지 않았는지 iteritems여부를 iter인수로 객체를 받아 들일 것입니다?
Mark Amery

@ MarkAmery 이것은로드가 아닌 덤프에 관한 것입니다. 덤프 할 데이터를 작성 하는 경우 ( 로드 하는 대신) 데이터가 무엇인지 확신 할 수 없습니다. 아이디어는 코드의 어느 곳에서나 나올 수 있도록하는 것이 었습니다.
n611x007

-2

이것을 확인하십시오 한다고 이런 비슷한 질문에 대답을

u- 접두사는 단지 유니 코드 문자열이 있음을 의미합니다. 실제로 문자열을 사용하면 데이터에 표시되지 않습니다. 인쇄 된 출력물에 버리지 마십시오.

예를 들어 다음을 시도하십시오.

print mail_accounts[0]["i"]

당신은 u를 볼 수 없습니다.


예를 들어 Py2에서 유니 코드 문자열을 포함하는 것을 형식화하려는 경우 사실이 아닙니다. 예를 들어 '{}'.format({u'x' : u'y'})여전히 u를 포함합니다.
Ponkadoodle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.