Python의 json 모듈, int 사전 키를 문자열로 변환


131

다음이 실행될 때 파이썬의 json 모듈 (2.6 이후 포함)이 int 사전 키를 문자열로 변환한다는 것을 발견했습니다.

>>> import json
>>> releases = {1: "foo-v0.1"}
>>> json.dumps(releases)
'{"1": "foo-v0.1"}'

덤프 및로드시 문자열을 구문 분석 할 필요없이 키를 int로 유지하는 쉬운 방법이 있습니까? json 모듈에서 제공하는 후크를 사용하는 것이 가능할 것이라고 생각하지만 여전히 파싱이 필요합니다. 내가 간과 한 논쟁이있을 수 있습니까? 건배, 차즈

하위 질문 : 답변 해 주셔서 감사합니다. json이 내가 두려워하는 것처럼 작동하는 것을 보았을 때 덤프 출력을 구문 분석하여 키 유형을 전달하는 쉬운 방법이 있습니까? 또한 덤프를 수행하는 코드와 서버에서 json 객체를 다운로드하고로드하는 코드는 모두 저에 의해 작성되었습니다.


23
json 키는 문자열이어야합니다
tonfa

답변:


86

이것은 당신을 물릴 수있는 다양한 매핑 컬렉션 간의 미묘한 차이점 중 하나입니다. JSON은 키를 문자열로 취급합니다. Python은 유형 만 다른 고유 키를 지원합니다.

파이썬에서 (그리고 분명히 Lua에서) 매핑에 대한 키 (각각 사전 또는 테이블)는 객체 참조입니다. Python에서는 변경 불가능한 유형이거나 __hash__메소드 를 구현하는 객체 여야합니다 . (Lua 문서는 변경 가능한 객체에 대해서도 객체의 ID를 해시 / 키로 자동으로 사용하고 동일한 문자열이 동일한 객체에 매핑되도록 문자열 인턴에 의존한다고 제안합니다.)

Perl, Javascript, awk 및 기타 여러 언어에서 해시, 연관 배열 또는 주어진 언어에 대해 호출되는 키는 문자열 (또는 Perl의 "스칼라")입니다. Perl $foo{1}, $foo{1.0}, and $foo{"1"}에서는 모든 동일한 매핑에 대한 참조가 있습니다 %foo--- 키는 스칼라로 평가 됩니다!

JSON은 Javascript 직렬화 기술로 시작되었습니다. (JSON은 약자 J 아바 S cript O bject N의 otation.) 물론 그것의 매핑 의미와 일치 그 매핑 표기 시맨틱을 구현한다.

직렬화의 양쪽 끝이 Python이 될 경우 피클을 사용하는 것이 좋습니다. 정말로 이것을 JSON에서 네이티브 Python 객체로 다시 변환해야한다면 몇 가지 선택이있을 것입니다. 먼저 try: ... except: ...사전 조회 실패시 키를 숫자로 변환하기 위해 ( )를 시도 할 수 있습니다. 또는 다른 쪽 끝 (이 JSON 데이터의 직렬 변환기 또는 생성기)에 코드를 추가하면 각 키 값에 대해 JSON 직렬화를 수행하도록 할 수 있습니다 .--- 이러한 값을 키 목록으로 제공합니다. (그런 다음 Python 코드는 먼저 키 목록을 반복하여 기본 Python 객체로 인스턴스화 / 역 직렬화 한 다음 매핑에서 값에 액세스하는 데 사용합니다.)


1
감사합니다. 불행히도 저는 Pickle을 사용할 수 없지만 목록에 대한 귀하의 아이디어는 훌륭합니다. 지금 구현하겠습니다, 아이디어를 응원합니다.
Charles Ritchie

1
(부수적으로 Python 1에서 1L (긴 정수) 및 1.0은 동일한 키에 매핑되지만 "1"(문자열)은 1 (정수) 또는 1.0 (부동) 또는 1L (긴 정수)과 동일하게 매핑되지 않습니다. ).
짐 데니스

5
Pickle 사용 권장 사항에주의하십시오. Pickle은 임의의 코드 실행을 초래할 수 있으므로 역 직렬화하는 데이터 소스가 본질적으로 신뢰할 수없는 경우 JSON과 같은 "안전한"직렬화 프로토콜을 사용해야합니다. 또한 프로젝트의 범위가 확장됨에 따라 예상했던 기능이 신뢰할 수있는 입력 만 가져 오기 시작하고 보안 고려 사항이 항상 재검토되는 것은 아닙니다.
AusIV 2016-06-24

55

아니요, JavaScript에는 숫자 키와 같은 것이 없습니다. 모든 개체 속성은 문자열로 변환됩니다.

var a= {1: 'a'};
for (k in a)
    alert(typeof k); // 'string'

이로 인해 호기심이 많은 동작이 발생할 수 있습니다.

a[999999999999999999999]= 'a'; // this even works on Array
alert(a[1000000000000000000000]); // 'a'
alert(a['999999999999999999999']); // fail
alert(a['1e+21']); // 'a'

자바 스크립트 객체는 Python과 같은 언어에서 이해하는 것처럼 실제로 적절한 매핑이 아니며, 문자열이 아닌 키를 사용하면 이상하게됩니다. 이것이 JSON이 필요하지 않은 경우에도 항상 명시 적으로 키를 문자열로 쓰는 이유입니다.


1
999999999999999999999로 변환 되지 않는 이유는 무엇 '999999999999999999999'입니까?
Piotr Dobrogost

4
@PiotrDobrogost JavaScript (많은 언어와 마찬가지로)는 임의로 큰 숫자를 저장할 수 없습니다. Number유형은이다 IEEE 754 배 부동 소수점 값 : 당신은 정수 정확도 2⁵³ (9007199254740992)을 저장할 수 있도록, 가수의 53 개 비트를 얻을; 그 이상의 정수는 다른 값으로 반올림됩니다 (따라서 9007199254740993 === 9007199254740992). 999999999999999999999는 1000000000000000000000으로 반올림되며 기본 toString표현은 1e+21입니다.
bobince

22

또는 json을 사용하여 인코딩하는 동안 사전을 [(k1, v1), (k2, v2)] 형식의 목록으로 변환하고 다시 디코딩 한 후 사전으로 다시 변환 할 수도 있습니다.


>>>> import json
>>>> json.dumps(releases.items())
    '[[1, "foo-v0.1"]]'
>>>> releases = {1: "foo-v0.1"}
>>>> releases == dict(json.loads(json.dumps(releases.items())))
     True
나는 이것이 json에서 다시 디코딩 한 후 모든 매개 변수를 사전으로 변환 할 것을 식별하기 위해 일종의 플래그를 갖는 것과 같은 더 많은 작업이 필요하다고 생각합니다.


중첩 된 dict 개체가없는 dict 개체에 대한 좋은 솔루션입니다!
Tom Yu

15

귀하의 구독에 대한 답변 :

다음을 사용하여 수행 할 수 있습니다. json.loads(jsonDict, object_hook=jsonKeys2int)

def jsonKeys2int(x):
    if isinstance(x, dict):
            return {int(k):v for k,v in x.items()}
    return x

이 함수는 중첩 된 딕셔너리에서도 작동하며 dict comprehension을 사용합니다.

값도 캐스트하려면 다음을 사용하십시오.

def jsonKV2int(x):
    if isinstance(x, dict):
            return {int(k):(int(v) if isinstance(v, unicode) else v) for k,v in x.items()}
    return x

값의 인스턴스를 테스트하고 문자열 객체 인 경우에만 캐스트합니다 (정확히 유니 코드).

두 함수 모두 키 (및 값)를 정수라고 가정합니다.

덕분에 :

사전 이해에서 if / else를 사용하는 방법?

사전에서 문자열 키를 int로 변환


이것은 훌륭했습니다. 제 경우에는 피클 링을 사용할 수 없으므로 압축을 사용할 수 있도록 byte_array 로의 변환을 통해 JSON을 사용하여 객체의 내장을 저장하고 있습니다. 키를 int로 변환하지 않을 때 난 그냥 ValueError를 무시하도록 변형 예, 그래서 나는, 혼합 키를 가지고있다
minillinim

11

나는 같은 문제에 물렸다. 다른 사람들이 지적했듯이 JSON에서 매핑 키는 문자열이어야합니다. 두 가지 중 하나를 수행 할 수 있습니다. 정수 문자열을 허용하는 demjson 과 같이 덜 엄격한 JSON 라이브러리를 사용할 수 있습니다 . 다른 프로그램 (또는 다른 언어로 된 다른 프로그램)이 읽을 수 없다면 괜찮습니다. 또는 다른 직렬화 언어를 사용할 수 있습니다. 피클은 권하지 않습니다. 읽기 어렵고 안전하도록 설계되지 않았습니다 . 대신, (거의) JSON의 상위 집합이며 정수 키를 허용하는 YAML을 제안합니다. (적어도 PyYAML그렇습니다 .)


2

을 사용하여 사전을 문자열 str(dict)로 변환 한 다음 다음을 수행하여 다시 dict로 변환합니다.

import ast
ast.literal_eval(string)

1

여기 내 해결책이 있습니다! 을 사용 object_hook했습니다. 중첩되었을 때 유용합니다.json

>>> import json
>>> json_data = '{"1": "one", "2": {"-3": "minus three", "4": "four"}}'
>>> py_dict = json.loads(json_data, object_hook=lambda d: {int(k) if k.lstrip('-').isdigit() else k: v for k, v in d.items()})

>>> py_dict
{1: 'one', 2: {-3: 'minus three', 4: 'four'}}

json 키를 int로 구문 분석하는 필터 만 있습니다. int(v) if v.lstrip('-').isdigit() else vjson 값에도 필터를 사용할 수 있습니다 .


1

나는 처음에 JSON에 의해 ​​덤프 될 수 있다고 가정하고 꽤 임의의 사전 (중첩 포함)에서 작동 할 것이라고 생각하는 Murmel의 답변을 매우 간단하게 확장했습니다. 정수로 해석 될 수있는 모든 키는 int로 캐스트됩니다. 의심 할 여지없이 이것은 매우 효율적이지 않지만 json 문자열에 저장하고로드하는 용도로 작동합니다.

def convert_keys_to_int(d: dict):
    new_dict = {}
    for k, v in d.items():
        try:
            new_key = int(k)
        except ValueError:
            new_key = k
        if type(v) == dict:
            v = _convert_keys_to_int(v)
        new_dict[new_key] = v
    return new_dict

원래 dict의 모든 키가 int로 캐스트 될 수있는 경우 정수라고 가정하면 json으로 저장 한 후 원래 사전을 반환합니다. 예 :

>>>d = {1: 3, 2: 'a', 3: {1: 'a', 2: 10}, 4: {'a': 2, 'b': 10}}
>>>convert_keys_to_int(json.loads(json.dumps(d)))  == d
True

-1

당신은 당신을 쓸 수 있습니다 json.dumps자신에 의해, 여기에서 예입니다 djson : encoder.py . 다음과 같이 사용할 수 있습니다.

assert dumps({1: "abc"}) == '{1: "abc"}'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.