bsonのdump(文字列表現)

人間が確認するためにbsonを文字列にdumpしたいことがある。

bsonはjsonではない

bsonはjsonではないのでjson.dumpsは使えない。

import json
import bson
from collections import ChainMap


person = ChainMap({"name": "foo"}, ChainMap({"age": 20}, {"_id": bson.ObjectId()}))

try:
    print(json.dumps(person))
except Exception as e:
    print("hmm", e)

エラーになる。

hmm ChainMap({'name': 'foo'}, ChainMap({'age': 20}, {'_id': ObjectId('5950d8f10ccee07b8eb563dc')})) is not JSON serializable

json.dumpsに小細工すると見た目が悪い

json.dumpsにdefaultなどを渡して小細工をすると文字列化はできるのだけれど。例えばdictではなくChainMapやOrderedDictみたいなMappingの型の値の場合に見た目が良くない。

print(json.dumps(person, indent=2, ensure_ascii=False, default=str))

これはよくない。

"ChainMap({'name': 'foo'}, ChainMap({'age': 20}, {'_id': ObjectId('5950d9560ccee07c0ec0d91f')}))"

もう少し真面目に書くとマシにはなる。

def default(d):
    if hasattr(d, "keys"):
        return dict(d)
    else:
        return str(d)


print(json.dumps(person, indent=2, ensure_ascii=False, default=default))

マシにはなる。

{
  "_id": "5950d9560ccee07c0ec0d91f",
  "name": "foo",
  "age": 20
}

bson.json_utilが便利かもしれない

bson.json_utilが便利かもしれない。

import bson.json_util as u

print(u.dumps(person, indent=2, ensure_ascii=False))

dumpsに対応するloadsもあったりはするのでこちらのほうが良いのかもしれない(とは言え、こちらの形式もMongoBoosterなどでおなじみの表現じゃないところがちょっと困る(もう少しbsonにencodingした時の状態に近い感じ))。

{
  "name": "foo",
  "_id": {
    "$oid": "5950d99a0ccee07c50d97fd2"
  },
  "age": 20
}