简体   繁体   English

如何在 Python 中使用 json.dumps() 将整数打印为十六进制字符串

[英]How to print integers as hex strings using json.dumps() in Python

Currently I am using the following code to print a large data structure目前我正在使用以下代码打印大型数据结构

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

I would like to see all the integers that get printed in hex instead of decimal.我希望看到所有以十六进制而不是十进制打印的整数。 Is that possible?那可能吗? It seems that there is no way to override the existing encoder for integers.似乎没有办法覆盖现有的整数编码器。 You can only provide a default for types not already handled by the JSONEncoder class, but no way to override how it encodes integers.您只能为 JSONEncoder class 尚未处理的类型提供默认值,但无法覆盖它对整数的编码方式。

I figured out I can override the default integer printing behavior using sys.displayhook if I was running in the command line but I am not.我发现如果我在命令行中运行,我可以使用sys.displayhook覆盖默认的 integer 打印行为,但我没有。

Just for reference the data structure is a mix bag of dicts, lists, strings, ints, etc. So that is why I went with the json.dumps() .仅供参考,数据结构是字典、列表、字符串、整数等的混合体。所以这就是我使用json.dumps()的原因。 The only other way I can think of doing it is to parse it myself and then I would be re-writing the json module.我能想到的唯一其他方法是自己解析它,然后我将重写 json 模块。

Update: So I ended up implementing it with serializing functions that just print a copy of the original data structure with all integer types converted to hex strings:更新:所以我最终使用序列化函数来实现它,这些函数只打印原始数据结构的副本,并将所有 integer 类型转换为十六进制字符串:

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

Thanks for the help.谢谢您的帮助。 I realize that I end up making an odict from a standard dict, but its just for printing so its fine for what I need.我意识到我最终从一个标准的字典中做出了一个命令,但它只是为了打印所以它很好地满足了我的需要。

A possible approach is to have a serialize function, which produces a copy of your dictionary on the fly and uses the standard json module to dump the string. 一种可能的方法是有一个serialize功能,从而产生上飞你的字典的副本,并使用标准json模块倾倒的字符串。 A preliminary implementation looks like: 初步实施如下:

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
}

Notice that this preliminary implementation does not work with lists, but this is easily changed. 请注意,此初步实现不适用于列表,但这很容易更改。

Some may claim that the approach is memory-intensive because it creates a copy of the original data. 有些人可能声称该方法是内存密集型的,因为它会创建原始数据的副本。 This may be the case, but if your data structure is that big, then maybe you should (a) not be using JSON, or (b) create a copy of the JSON module in your working directory and tailor it to your needs. 可能是这种情况,但如果您的数据结构很大,那么您可能应该(a)不使用JSON,或者(b)在工作目录中创建JSON模块的副本并根据您的需要进行定制。

Cheers. 干杯。

Octal and hexadecimal formats are not supported in JSON . JSON不支持八进制和十六进制格式。

You could use YAML instead. 你可以改用YAML

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

Or without wrapping integers: 或者没有包装整数:

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}

This is admittedly not the cleanest or most elegant way to do this, but it was the quickest for me as I didn't have to look into JSONEncoder and JSONDecoder诚然,这不是执行此操作的最干净或最优雅的方法,但对我来说这是最快的,因为我不必查看JSONEncoderJSONDecoder

def obj_to_hex(obj: Any):
    """Recursively convert integers to ascii hex"""
    if isinstance(obj, int):
        return '0x%.8x' % obj
    if isinstance(obj, dict):
        return {k: obj_to_hex(v) for k, v in obj.items()}
    if isinstance(obj, list):
        return [obj_to_hex(l) for l in obj]
    return obj


def obj_from_hex(obj: Any):
    """Recursively convert ascii hex values to integers"""
    if all((isinstance(obj, str), obj.startswith('0x'))):
            return int(obj, 16)
    if isinstance(obj, dict):
        return {k: obj_from_hex(v) for k, v in obj.items()}
    if isinstance(obj, list):
        return [obj_from_hex(l) for l in obj]
    if isinstance(obj, int):
        return '0x%.8x' % obj
    return obj


def json_dump_hex(obj, stream, **kwargs):
    return json.dump(obj_to_hex(obj), stream, **kwargs)


def json_dumps_hex(obj, **kwargs):
    return json.dumps(obj_to_hex(obj), **kwargs)


def json_load_hex(stream, **kwargs):
    return obj_from_hex(json.load(stream, **kwargs))


def json_loads_hex(buf, **kwargs):
    return obj_from_hex(json.loads(buf, **kwargs))

This gives you the following behavior这给你以下行为

obj = {'base_address': 4096, 'base_length': 4096, 'mappings': {'text': 16384, 'bss': 65536}}
print(json_dumps_hex(obj, indent=2))
print(json.dumps(obj, indent=2))

Outputs:输出:

{
  "base_address": "0x00001000",
  "base_length": "0x00001000",
  "mappings": {
    "text": "0x00004000",
    "bss": "0x00010000"
  }
}
{
  "base_address": 4096,
  "base_length": 4096,
  "mappings": {
    "text": 16384,
    "bss": 65536
  }
}

If you really wanted to, you could then use something like this, to not need to use the wrappers- but beware this will impact all calls:如果你真的想要,你可以使用这样的东西,不需要使用包装器 - 但要注意这会影响所有调用:

json.loads = json_loads_from_hex
json.load = json_load_from_hex
json.dump = json_dump_to_hex
json.dumps = json_dumps_to_hex

You could probably make this a little cleaner using a decorator or a contextlib.contextmanager to reduce the clunkiness a bit您可以使用装饰器或contextlib.contextmanager来稍微减少笨重感,从而使它更简洁一些

Note: After seeing that json.load() and json.loads() support a parse_int callable kwarg, that is probably what most people want in most cases.注意:在看到json.load()json.loads()支持parse_int可调用 kwarg 后,这可能是大多数人在大多数情况下想要的。 An exception to this is where you want to emit JSON with hex so that another tool can read it (eg jq )一个例外是你想用十六进制发出 JSON 以便另一个工具可以读取它(例如jq

Note: Just realized this is roughly what @plazgoth said... oops注意:刚刚意识到这大致就是@plazgoth所说的......哎呀

You can't override the existing encoder for integers...but there might be another way to get what you want. 你不能覆盖整数的现有编码器...但可能有另一种方法来获得你想要的。 What about something like this: 这样的事情怎么样:

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

Results in: 结果是:

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

Note: This isn't especially "robust" (fails on numbers embedded in strings, floats, etc.), but might be good enough for what you want (You could also enhance the regex here so it would work in a few more cases). 注意:这不是特别“强大”(嵌入在字符串,浮点数等中的数字失败),但可能对你想要的东西足够好(你也可以在这里增强正则表达式,这样它可以在更多的情况下工作)。

Dirty hack for Python 2.7, I wouldn't recomend to use it: 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))

Output: 输出:

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

On Python 3 __builtin__ module is now builtins , but I can't test it (ideone.com fails with ImportError: libz.so.1 ...) 在Python 3 __builtin__模块现在是builtins ,但我无法测试它(ideone.com失败,导入错误:libz.so.1 ...)

You could always reparse the json, where you do have some control over int parsing, so that you can override int repr: 你总是可以重新解析json,你可以对int解析有一些控制权,这样你就可以覆盖int repr:

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

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

And using the data as in Gerrat's answer, the output is: 并且使用Gerrat的答案中的data ,输出是:

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

One-liner 一衬垫

If you don't mind your hex strings quoted, use this one-liner: 如果您不介意引用的六角形字符串,请使用以下单行:

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

Output (using Gerrat's data again): 输出(再次使用Gerrat的data ):

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

This is a better answer than my previous post as I've dealt with getting a pretty-printed result. 这是一个比我以前的帖子更好的答案,因为我已经处理了一个漂亮的打印结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM