Как напечатать целые числа в виде шестнадцатеричных строк, используя json.dumps () в Python - PullRequest
6 голосов
/ 01 февраля 2012

В настоящее время я использую следующий код для печати большой структуры данных

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

Я хотел бы видеть все целые числа, которые печатаются в шестнадцатеричном, а не в десятичном виде. Это возможно? Кажется, что нет способа переопределить существующий кодер для целых чисел. Вы можете предоставить значение по умолчанию только для типов, еще не обработанных классом JSONEncoder, но не можете переопределить способ кодирования целых чисел.

Я понял, что могу переопределить поведение целочисленной печати по умолчанию, используя sys.displayhook , если я работал в командной строке, но это не так.

Просто для справки, структура данных представляет собой набор из множества слов, списков, строк, целых чисел и т. Д. Вот почему я выбрал json.dumps () . Единственный другой способ сделать это - проанализировать его сам, и тогда я переписываю модуль json.

Обновление: В итоге я реализовал его с сериализацией функций, которые просто распечатывают копию исходной структуры данных со всеми целочисленными типами, преобразованными в шестнадцатеричные строки:

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))

Спасибо за помощь. Я понимаю, что в итоге я отказываюсь от стандартного диктанта, но это просто для печати, так что это прекрасно для того, что мне нужно.

Ответы [ 6 ]

2 голосов
/ 01 февраля 2012

Восьмеричные и шестнадцатеричные форматы не поддерживаются в JSON .

Вместо этого вы можете использовать YAML.

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

Или без переноса целых чисел:

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}
2 голосов
/ 01 февраля 2012

Возможный подход - использовать функцию serialize, которая создает копию вашего словаря на лету и использует стандартный модуль json для вывода строки.Предварительная реализация выглядит следующим образом:

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)

output:

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

Обратите внимание, что эта предварительная реализация не работает со списками, но ее легко изменить.

Некоторыеможет утверждать, что этот подход требует большого объема памяти, поскольку создает копию исходных данных.Это может иметь место, но если ваша структура данных настолько велика, то, возможно, вам следует (а) не использовать JSON или (б) создать копию модуля JSON в вашем рабочем каталоге и адаптировать ее к вашим потребностям.

Приветствия.

1 голос
/ 01 февраля 2012

Вы не можете переопределить существующий кодер для целых чисел ... но может быть другой способ получить то, что вы хотите. Что-то вроде этого:

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))

Результат:

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

Примечание: это не особенно "надежно" (не работает с числами, встроенными в строки, числа с плавающей точкой и т. Д.), Но может быть достаточно для того, что вы хотите (вы можете также улучшить регулярное выражение здесь, чтобы оно работало еще несколько случаев).

0 голосов
/ 02 февраля 2012

Однострочный

Если вы не возражаете против приведенных вами шестнадцатеричных строк, используйте этот однострочный:

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

Вывод (используя Gerrat's data снова):

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

Это лучший ответ, чем мой предыдущий пост, так как я имел дело с довольно печатным результатом.

0 голосов
/ 02 февраля 2012

Вы всегда можете повторно проанализировать json, где у вас есть некоторый контроль над анализом int, так что вы можете переопределить int repr:

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

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

И, используя data, как в ответе Геррата, получим:

{u'test': 0x21, u'this': 0x63, u'something bigger': [0x1, 0x2, 0x3, {u'a': 0x2c}]}
0 голосов
/ 01 февраля 2012

Грязный хак для Python 2.7, я бы не рекомендовал его использовать:

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))

Вывод:

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

На Python 3 __builtin__ модуль теперь builtins, но я не могу это проверить (ideone.com не удается с помощью ImportError: libz.so.1 ...)

...