可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
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.
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(). 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.
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:
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.
回答1:
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. 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.
Cheers.
回答2:
Octal and hexadecimal formats are not supported in JSON.
You could use YAML
instead.
>>> 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}
回答3:
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).
回答4:
Dirty hack for Python 2.7, I wouldn't recomend to use it:
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 ...)
回答5:
You could always reparse the json, where you do have some control over int parsing, so that you can override 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:
{u'test': 0x21, u'this': 0x63, u'something bigger': [0x1, 0x2, 0x3, {u'a': 0x2c}]}
回答6:
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):
{
"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.