2012-02-01 29 views
5

Attualmente sto usando il seguente codice per stampare una grande struttura di datiCome stampare interi come stringhe esadecimali utilizzando json.dumps() in Python

print(json.dumps(data, indent=4)) 

mi piacerebbe vedere tutti gli interi che vengono stampati in esadecimale invece di decimale. È possibile? Sembra che non ci sia modo di sovrascrivere il codificatore esistente per i numeri interi. È possibile fornire un valore predefinito solo per tipi non già gestiti dalla classe JSONEncoder, ma non è possibile sovrascrivere il modo in cui codifica gli interi.

Ho scoperto che è possibile ignorare il comportamento di stampa dei numeri interi predefinito utilizzando sys.displayhook se ero in esecuzione nella riga di comando, ma non lo sono.

Solo per riferimento la struttura dati è un miscuglio di dit, elenchi, stringhe, int, ecc. Ecco perché sono andato con lo json.dumps(). L'unico altro modo in cui posso pensare di farlo è analizzarlo da solo e quindi dovrei riscrivere il modulo JSON.

Aggiornamento: Così ho finito per la sua attuazione con funzioni che solo stampare una copia della struttura dati originale con tutti i tipi interi convertite in stringhe esadecimali serializzazione:

def odprint(self, hexify=False): 
    """pretty print the ordered dictionary""" 
    def hexify_list(data): 
     _data = [] 
     for i,v in enumerate(data): 
      if isinstance(v, (int,long)): 
       _data.insert(i,hex(v)) 
      elif isinstance(v,list): 
       _data.insert(i, hexify_list(v)) 
      else: 
       _data.insert(i, val) 
     return _data 

    def hexify_dict(data): 
     _data = odict() 
     for k,v in data.items(): 
      if isinstance(v, (dict,odict)): 
       _data[k] = hexify_dict(v) 
      elif isinstance(v, (int, long)): 
       _data[k] = hex(v) 
      elif isinstance(v,list): 
       _data[k] = hexify_list(v) 
      else: 
       _data[k] = v 
     return _data 

    if hexify: 
     print(json.dumps(hexify_dict(self), indent=4)) 
    else: 
     print(json.dumps(self, indent=4)) 

Grazie per l'aiuto. Mi rendo conto che finisco per fare un odetto da un dettato standard, ma è solo per la stampa, quindi va bene per quello che mi serve.

+0

[Le forme ottale e esadecimale non sono consentite in JSON] (http://tools.ietf.org/html/rfc4627#section-2.4) – jfs

+0

nota: le funzioni hexify _ *() possono perdere dati. Se segui questa strada puoi usare [qualcosa di simile] (http://ideone.com/G2DJG) – jfs

+0

Puoi spiegare come può perdere i dati? – Plazgoth

risposta

2

Un approccio possibile è avere una funzione serialize, che produce una copia del tuo dizionario y al volo e utilizza il modulo standard json per scaricare la stringa. Un'implementazione preliminare assomiglia:

import json 

def serialize(data): 
    _data = {} 
    for k, v in data.items(): 
     if isinstance(v, int): 
      _data[k] = hex(v) 
     else: 
      _data[k] = v 
    return json.dumps(_data, indent=4) 


if __name__ == "__main__": 
    data = {"a":1, "b":2.0, "c":3} 
    print serialize(data) 

uscita:

{ 
    "a": "0x1", 
    "c": "0x3", 
    "b": 2.0 
} 

Si noti che questa implementazione preliminare non funziona con le liste, ma questo è facilmente cambiata.

Alcuni possono affermare che l'approccio richiede molta memoria in quanto crea una copia dei dati originali. Potrebbe essere il caso, ma se la struttura dei dati è così grande, forse dovresti (a) non usare JSON, o (b) creare una copia del modulo JSON nella tua directory di lavoro e adattarla alle tue esigenze.

Cheers.

+0

Come faresti funzionare con le liste? – Plazgoth

+0

L'argomento della memoria non è valido nel mio caso. Quindi mi piace questo approccio. Lo sto testando e cercando di capire come farlo funzionare per liste e liste di liste. La mia struttura dati non è enorme ma è brutta :) – Plazgoth

1

Non è possibile sovrascrivere il codificatore esistente per i numeri interi ... ma potrebbe esserci un altro modo per ottenere ciò che si desidera. Che dire qualcosa di simile:

import json 
import re 

data = {'test': 33, 'this': 99, 'something bigger':[1,2,3, {'a':44}]} 
s = json.dumps(data, indent=4) 
print(re.sub('(\d+)', lambda i: hex(int(i.group(0))),s)) 

risultati in:

{ 
    "test": 0x21, 
    "this": 0x63, 
    "something bigger": [ 
     0x1, 
     0x2, 
     0x3, 
     { 
      "a": 0x2c 
     } 
    ] 
} 

Nota: Questo non è particolarmente "robusto" (non su numeri incorporati nelle stringhe, galleggianti, ecc), ma potrebbe essere abbastanza buono per quello che vuoi (Potresti anche migliorare la regex qui in modo che funzioni in qualche altro caso).

+0

Grazie a questo sembra promettente, lo digerirò, testarlo e tornare da te. – Plazgoth

+0

Quindi funziona ma converte i numeri anche quando fanno parte di una stringa come 'x86_64' diventa' x0x54_0x40' Ho speso qualche minuto giocherellando con l'espressione regolare per provare e correggere ma ho rinunciato :) – Plazgoth

0

mod sporco per Python 2.7, Non consiglierei di usarlo:

import __builtin__ 

_orig_str = __builtin__.str 

def my_str(obj): 
    if isinstance(obj, (int, long)): 
     return hex(obj) 
    return _orig_str(obj) 
__builtin__.str = my_str 

import json 

data = {'a': [1,2,3], 'b': 4, 'c': 16**20} 
print(json.dumps(data, indent=4)) 

uscita:

{ 
    "a": [ 
     0x1, 
     0x2, 
     0x3 
    ], 
    "c": 0x100000000000000000000L, 
    "b": 0x4 
} 

Su Python 3 __builtin__ modulo è ora builtins, ma non riesco a provare (ideone.com non riesce con ImportError: libz.so.1 ...)

+0

Sto usando Python 2.6 attualmente. Ma grazie per l'idea. – Plazgoth

2

I formati ottale ed esadecimale non sono supportati in JSON.

È possibile utilizzare invece YAML.

>>> import json, yaml 
>>> class hexint(int): 
...  def __str__(self): 
...   return hex(self) 
... 
>>> json.dumps({"a": hexint(255)}) 
'{"a": 0xff}' 
>>> yaml.load(_) 
{'a': 255} 

O senza interi avvolgenti:

import yaml 

def hexint_presenter(dumper, data): 
    return dumper.represent_int(hex(data)) 
yaml.add_representer(int, hexint_presenter) 

print yaml.dump({"a": 255}), # -> {a: 0xff} 
assert yaml.load('{a: 0xff}') == {"a": 255} 
+0

Yaml non fa parte dell'installazione Python sul server che sto usando, e non voglio aggiungere il modulo localmente per ora. Ma questo sembra buono. – Plazgoth

+0

@Plazgoth: non sarà possibile caricare [numeri esadecimali come numeri interi con json] (http://ideone.com/cInsT). – jfs

+0

Ah, capisco il tuo commento. In realtà non importerò l'output di questo come json. Questo è solo un tentativo di stampare la struttura dei dati sullo stdout in modo leggibile. Grazie, avrei dovuto dirlo nella mia domanda. – Plazgoth

0

Si può sempre di analisi JSON, in cui si ha un certo controllo su int analisi, in modo che è possibile ignorare int repr:

class hexint(int): 
    def __repr__(self): 
    return "0x%x" % self 

json.loads(json.dumps(data), parse_int=hexint) 

E usando data come nella risposta di Gerrat, l'output è:

{u'test': 0x21, u'this': 0x63, u'something bigger': [0x1, 0x2, 0x3, {u'a': 0x2c}]} 
0

One-liner

Se non ti dispiace le corde hex citato, utilizzare questo uno-liner:

print(json.dumps(eval(str(json.loads(json.dumps(data), parse_int=lambda i:hex(int(i))))), indent=4)) 

uscita (usando ancora una volta di Gerrat data):

{ 
    "test": "0x21", 
    "this": "0x63", 
    "something bigger": [ 
     "0x1", 
     "0x2", 
     "0x3", 
     { 
      "a": "0x2c" 
     } 
    ] 
} 

Questa è una risposta migliore rispetto al mio post precedente poiché mi sono occupato di ottenere un risultato abbastanza stampato.

+0

Funziona bene, ma non mantiene l'ordine nei dati che è un ordine ordinato. – Plazgoth

Problemi correlati