Python'un json modülü, int sözlük anahtarlarını dizelere dönüştürür


132

Aşağıdakiler çalıştırıldığında, python'un json modülünün (2.6'dan beri dahil edilmiştir) int sözlük anahtarlarını dizelere dönüştürdüğünü buldum.

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

Dump ve load'da dizeyi ayrıştırmaya gerek kalmadan anahtarı int olarak korumanın kolay bir yolu var mı? Json modülü tarafından sağlanan kancaları kullanarak mümkün olacağına inanıyorum, ancak yine de bunun ayrıştırılması gerekiyor. Muhtemelen gözden kaçırdığım bir tartışma var mı? şerefe, chaz

Alt soru: Cevaplar için teşekkürler. JSON'un korktuğum gibi çalıştığını görmek, belki dökümlerin çıktısını ayrıştırarak anahtar türü aktarmanın kolay bir yolu var mı? Ayrıca dökümü yapan kodun ve json nesnesini bir sunucudan indirip yükleyen kodun benim tarafımdan yazıldığını not etmeliyim.


23
json anahtarlarının dizge olması gerekir
tonfa

Yanıtlar:


87

Bu, sizi ısırabilecek çeşitli harita koleksiyonları arasındaki ince farklılıklardan biridir. JSON, anahtarları dizeler olarak ele alır; Python, yalnızca tür olarak farklı olan farklı anahtarları destekler.

Python'da (ve görünüşe göre Lua'da) bir eşlemenin anahtarları (sırasıyla sözlük veya tablo) nesne referanslarıdır. Python'da değişmez türler olmalı veya bir __hash__yöntemi uygulayan nesneler olmalıdır . (Lua belgeleri, nesnenin kimliğini, değişebilir nesneler için bile otomatik olarak bir karma / anahtar olarak kullandığını ve eşdeğer dizelerin aynı nesnelerle eşleşmesini sağlamak için dizge interning'e dayandığını önerir).

Perl, Javascript, awk ve diğer birçok dilde, özetlerin anahtarları, ilişkilendirilebilir diziler veya verilen dil için çağrılanlar dizelerdir (veya Perl'de "skalarlar"). Perl'de $foo{1}, $foo{1.0}, and $foo{"1"}aynı eşlemeye yapılan tüm referanslar %foo--- anahtar bir skaler olarak değerlendirilir !

JSON, bir Javascript serileştirme teknolojisi olarak başladı. (JSON, J Ava S cript O Nesne , N -rotasyon). Doğal olarak, eşleştirme semantik ile tutarlıdır, eşleştirme gösterim için semantik uygular.

Serileştirmenizin her iki ucu da Python olacaksa, turşu kullanmanız daha iyi olur. Bunları gerçekten JSON'dan yerel Python nesnelerine dönüştürmeniz gerekiyorsa, sanırım birkaç seçeneğiniz var. İlk try: ... except: ...olarak, sözlükte bir arama hatası olması durumunda herhangi bir anahtarı sayıya dönüştürmeyi deneyebilirsiniz ( ). Alternatif olarak, diğer uca (bu JSON verilerinin serileştiricisi veya oluşturucusu) kod eklerseniz, anahtar değerlerinin her birinde bir JSON serileştirmesi gerçekleştirmesini sağlayabilirsiniz - bunları bir anahtar listesi olarak sağlar. (Daha sonra Python kodunuz önce anahtarlar listesi üzerinde yineler, bunları yerel Python nesneleri olarak somutlaştırır / serisini kaldırır ... ve sonra bunları eşlemeden değerlere erişmek için kullanır).


1
Bunun için teşekkürler. Maalesef Pickle'ı kullanamıyorum ama listeyle ilgili fikriniz harika. Bunu şimdi uygulayacak, fikir için teşekkürler.
Charles Ritchie

1
(Bu arada, Python 1, 1L (uzun tamsayı) ve 1.0'da aynı anahtarla eşlenir; ancak "1" (bir dize), 1 (tam sayı) veya 1.0 (kayan) veya 1L (uzun tamsayı) ile aynı şeyle eşleşmez ).
Jim Dennis

5
Turşu kullanma tavsiyesi konusunda dikkatli olun. Pickle, rastgele kod yürütülmesine neden olabilir, bu nedenle serisini kaldırdığınız verilerin kaynağı doğal olarak güvenilir değilse, JSON gibi "güvenli" bir serileştirme protokolüne bağlı kalmalısınız. Ayrıca, projelerin kapsamı genişledikçe, bazen beklediğiniz işlevlerin yalnızca güvenilir girdi alacağını ve kullanıcı tarafından sağlanan girdileri almaya başlayacağını ve güvenlik hususlarının her zaman yeniden gözden geçirilmediğini unutmayın.
AusIV

55

Hayır, JavaScript'te Sayı tuşu diye bir şey yoktur. Tüm nesne özellikleri String'e dönüştürülür.

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

Bu, bazı ilginç görünen davranışlara yol açabilir:

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

JavaScript Nesneleri, Python gibi dillerde anladığınız gibi gerçekten uygun eşlemeler değildir ve String olmayan anahtarların kullanılması tuhaflığa neden olur. Bu nedenle JSON, gerekli görünmese bile anahtarları her zaman açıkça dizeler olarak yazar.


1
Neden 999999999999999999999dönüştürülmedi '999999999999999999999'?
Piotr Dobrogost

4
@PiotrDobrogost JavaScript (birçok dil gibi) keyfi olarak büyük sayıları saklayamaz. NumberTürü, bir IEEE 754 çift kayan nokta değeri: Tamsayı hassasiyetle 2⁵³ (9007199254740992) saklayabilen, böylece 53 bit mantis olsun; bunun ötesinde tamsayılar diğer değerlere yuvarlanır (dolayısıyla 9007199254740993 === 9007199254740992). 999999999999999999999, 1000000000000000000000'e yuvarlanır, bunun için varsayılan toStringgösterimdir 1e+21.
bobince

22

Alternatif olarak, json kullanarak kodlarken sözlüğü [(k1, v1), (k2, v2)] formatlı bir listeye dönüştürmeyi ve kod çözdükten sonra tekrar sözlüğe dönüştürmeyi deneyebilirsiniz.


>>>> import json
>>>> json.dumps(releases.items())
    '[[1, "foo-v0.1"]]'
>>>> releases = {1: "foo-v0.1"}
>>>> releases == dict(json.loads(json.dumps(releases.items())))
     True
Bunun json'dan geri kod çözdükten sonra tüm parametrelerin sözlüğe dönüştürüleceğini belirlemek için bir tür bayrak gibi biraz daha çalışmaya ihtiyaç duyacağına inanıyorum.


İç içe dikte nesneler içermeyen dikte nesneleri için iyi bir çözüm!
Tom Yu

15

Alt sorunuzu yanıtlamak:

Kullanılarak gerçekleştirilebilir 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

Bu işlev aynı zamanda iç içe yerleştirilmiş dikteler için de çalışır ve bir dikte anlama kullanır.

Değerleri de dönüştürmek istiyorsanız, şunu kullanın:

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

Değerlerin örneğini test eden ve bunları yalnızca dize nesneleriyse dönüştüren (tam olarak unicode).

Her iki işlev de anahtarların (ve değerlerin) tamsayı olduğunu varsayar.

Sayesinde:

Sözlük anlamında if / else nasıl kullanılır?

Sözlük'te bir dize anahtarını int'e dönüştürme


Bu harikaydı. Benim durumumda dekapaj kullanılamaz, bu yüzden sıkıştırmayı kullanabilmek için JSON kullanarak bir byte_array'e dönüştürme yoluyla bir nesnenin cesaretini kaydediyorum. Karışık anahtarlarım var, bu yüzden örneğinizi anahtar bir int
minillinim'e

11

Aynı sorundan ısırıldım. Diğerlerinin de belirttiği gibi, JSON'da eşleme anahtarları dizeler olmalıdır. İki şeyden birini yapabilirsiniz. Tamsayı dizelerine izin veren demjson gibi daha az katı bir JSON kitaplığı kullanabilirsiniz . Başka hiçbir program (veya başka dillerde başka hiçbir program) okumayacaksa, o zaman iyi olmalısın. Veya farklı bir serileştirme dili kullanabilirsiniz. Turşu önermem. Okuması zordur ve güvenli olacak şekilde tasarlanmamıştır . Bunun yerine, JSON'un (neredeyse) bir üst kümesi olan ve tamsayı anahtarlarına izin veren YAML'yi öneririm. (En azından PyYAML yapar.)


2

Sözlüğü kullanarak dizeye str(dict)dönüştürün ve ardından bunu yaparak tekrar dikteye dönüştürün:

import ast
ast.literal_eval(string)

1

İşte benim çözümüm! Kullandım object_hook, iç içe geçtiğinde kullanışlıdırjson

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

Yalnızca json anahtarını int'e ayrıştırmak için filtre vardır. int(v) if v.lstrip('-').isdigit() else vJson değeri için filtre de kullanabilirsiniz .


1

Murmel'in cevabının, ilk etapta JSON tarafından atılabileceğini varsayarak oldukça keyfi bir sözlükte (iç içe geçmiş dahil) işe yarayacağını düşündüğüm çok basit bir uzantı yaptım. Tamsayı olarak yorumlanabilen tüm anahtarlar int'e dönüştürülecektir. Hiç şüphe yok ki bu çok verimli değil, ancak json dizelerine depolama ve yükleme yapma amaçlarım için çalışıyor.

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

Orijinal diktedeki tüm anahtarların int'e dönüştürülebiliyorlarsa tamsayılar olduğunu varsayarsak, bu, json olarak depolandıktan sonra orijinal sözlüğü döndürür. Örneğin

>>>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.dumpsKendiniz yazabilirsiniz , işte djson'dan bir örnek : encoder.py . Bunu şu şekilde kullanabilirsiniz:

assert dumps({1: "abc"}) == '{1: "abc"}'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.