在通过bson进行数据传输时经常会遇到BSON无法转化自定义类型的异常。从其代码中没有发现提供转换接口,一般只能在外界先将数据转化之后,再交由bson进行编码。
很多时候这种转化代码并不方便,看着也不简洁。因此考虑直接在BSON库中增加自定义类型的转化处理。
修改bson/__init__.py
bson主体逻辑都在bson/__init__.py
内,从其代码分析,如果能动态增加bson._ENCODERS
、bson._ELEMENT_GETTER
中的匹配关系就能够实现自定义类型的转化处理。
def update_bson(identifier, klass, encoder_func, decoder_dunc):
bson._ENCODERS.update({
klass: encoder_func,
})
bson._ELEMENT_GETTER.update({
identifier: decoder_dunc,
})
def encoder_func(name, value, dummy0, dummy1):
pass
def decoder_func(data, position, dummy0, dummy1, dummy2):
pass
bson数据进行编码时其格式为,
ID + NAME + DATA1 + DATA2 + ...
解析的时候也是按照写入顺序进行解析。
实现一个简单的样例,
IDENTIFIER = b'\x91'
class Foobar(object):
def __init__(self, value):
self.value = value
def encode_foobar(name, value, dummy0, dummy1):
return IDENTIFIER + name + bson._PACK_FLOAT(value.value)
def decode_foobar(data, position, dummy0, dummy1, dummy2):
step = 8
end = position + step
value = bson._UNPACK_FLOAT(data[position:end])[0]
return Foobar(value), end
在不更新之前执行,
print bson.BSON(bson.BSON.encode({'foobar': Foobar(42)})).decode()
会遇到如下异常,
bson.errors.InvalidDocument: cannot convert value of type <class '__main__.Foobar'> to bson
在更新处理之后,
update_bson(IDENTIFIER, Foobar, encode_foobar, decode_foobar)
print bson.BSON(bson.BSON.encode({'foobar': Foobar(42)})).decode()
可以获得正常输出,
{u'foobar': <__main__.Foobar object at 0x02376570>}
处理cbson
在存在cbson时,bson/__init__.py
中的编解码函数会被替换成cbson
中的实现。因此无法再动态修改,如果想继续使用cbson,那么就需要修改bson/_cbsonmodule.c
中的实现。
encode数据需要修改,
static int _write_element_to_buffer(PyObject* self, buffer_t buffer,
int type_byte, PyObject* value,
unsigned char check_keys,
const codec_options_t* options)
decode数据需要修改,
static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer,
unsigned* position, unsigned char type,
unsigned max, const codec_options_t* options)
在上述两个函数中分别实现对应的编解码逻辑即可。如果不想每次增加新类型都需要重新编译修改cbson中的实现,那么就可以考虑在cbson中遇到这些自定义数据时反向调用脚本层中的处理逻辑。