Pymongo:无法编码 decimal.Decimal 类型的 object?
Pymongo: Cannot encode object of type decimal.Decimal?
之后试图insert_one变成了collection。
我收到此错误:
bson.errors.InvalidDocument: cannot encode object: Decimal('0.16020'), of type: <class 'decimal.Decimal'>
当 JSON 不包含 decimal.Decimal
object 时代码运行良好。如果有解决方案,您能否考虑以递归方式对其进行编码,以使整个 python 字典 json_dic
兼容插入 MongoDB (因为有不止一次的实例json.dic
条目中的 class decimal.Decimal)。
如有任何帮助,我们将不胜感激!
编辑 1: 这是我正在处理的 json
import simplejson as json
from pymongo import MongoClient
json_string = '{"A" : {"B" : [{"C" : {"Horz" : 0.181665435,"Vert" : 0.178799435}}]}}'
json_dict = json.loads(json_string)
this_collection.insert_one(json_dict)
这会产生
bson.errors.InvalidDocument: cannot encode object: Decimal('0.181665435'), of type: <class 'decimal.Decimal'>
编辑 2: 不幸的是,我上面的例子简化了我现有的 JSON 和@ 提供的答案(尽管使用 Json 上面)抛出一个错误:
AttributeError: 'decimal.Decimal' object has no attribute 'items'
我的实际 Json,所以我在这里提供完整的 Json 希望找出问题所在(也 as a screen-shot):
json_string =
'
{
"Setting" : {
"GridOptions" : {
"Student" : "HighSchool",
"Lesson" : 1,
"Attended" : true
},
"Grades" : [
80,
50.75
],
"Count" : 2,
"Check" : "Coursework",
"Passed" : true
},
"Slides" : [
{
"Type" : "ABC",
"Duration" : 1.5
},
{
"Type" : "DEF",
"Duration" : 0.5
}
],
"Work" : {
"Class" : [
{
"Time" : 123456789,
"Marks" : {
"A" : 50,
"B" : 100
}
}
],
"CourseWorkDetail" : [
{
"Test" : {
"Mark" : 0.987654321
},
"ReadingDate" : "Feb162006",
"Reading" : 300.001,
"Values" : [
[
0.98765
],
[
-0.98765
]
]
},
{
"Test" : {
"Mark" : 0.123456789
},
"ReadingDate" : "Jan052010",
"Reading" : 200.005,
"Values" : [
[
0.12345
],
[
-0.12345
]
]
}
]
},
"listing" : 5
}
'
编辑 3:
作为对下面答案的补充,您可以像这样在字典中递归迭代并使用答案
中的函数
def iterdict(dict_items, debug_out):
for k, v in dict_items.items():
if isinstance(v):
iterdict(v)
else:
dict_items[k] = convert_decimal(v)
return dict_items
Pymongo 无法识别 Decimal
- 这就是您收到错误的原因。
正确的 pymongo 插入是 coll.insert_one({"number1": Decimal128('8.916')})
。
您还需要导入 - from bson import Decimal128
现在,如果您想在不将 Decimal
更改为 Decimal128 的情况下处理 JSON 文件,您可以修改导入语句。
from bson import Decimal128 as Decimal
coll.insert_one({"number1": Decimal('8.916')})
编辑:
convert_decimal()
函数将在复杂的字典结构中执行 Decimal 到 Decimal128 的转换:
import simplejson as json
from pymongo import MongoClient
from decimal import Decimal
from bson.decimal128 import Decimal128
def convert_decimal(dict_item):
# This function iterates a dictionary looking for types of Decimal and converts them to Decimal128
# Embedded dictionaries and lists are called recursively.
if dict_item is None: return None
for k, v in list(dict_item.items()):
if isinstance(v, dict):
convert_decimal(v)
elif isinstance(v, list):
for l in v:
convert_decimal(l)
elif isinstance(v, Decimal):
dict_item[k] = Decimal128(str(v))
return dict_item
db = MongoClient()['mydatabase']
json_string = '{"A" : {"B" : [{"C" : {"Horz" : 0.181665435,"Vert" : 0.178799435}}]}}'
json_dict = json.loads(json_string, use_decimal=True)
db.this_collection.insert_one(convert_decimal(json_dict))
print(db.this_collection.find_one())
给出:
{'_id': ObjectId('5ea743aa297c9ccd52d33e05'), 'A': {'B': [{'C': {'Horz': Decimal128('0.181665435'), 'Vert': Decimal128('0.178799435')}}]}}
原创:
要将小数转换为 MongoDB 满意的 Decimal128,请将其转换为字符串,然后再转换为 Decimal128。此代码段可能会有所帮助:
from pymongo import MongoClient
from decimal import Decimal
from bson.decimal128 import Decimal128
db = MongoClient()['mydatabase']
your_number = Decimal('234.56')
your_number_128 = Decimal128(str(your_number))
db.mycollection.insert_one({'Number': your_number_128})
print(db.mycollection.find_one())
给出:
{'_id': ObjectId('5ea6ec9b52619c7b39b851cb'), 'Number': Decimal128('234.56')}
from bson.decimal128 import Decimal128, create_decimal128_context
from decimal import localcontext
decimal128_ctx = create_decimal128_context()
with localcontext(decimal128_ctx) as ctx:
horiz_val = Decimal128(ctx.create_decimal("0.181665435"))
vert_val = Decimal128(ctx.create_decimal("0.178799435"))
doc = { 'A': { 'B': [ { 'C': { 'Horiz': horiz_val, 'Vert': vert_val } } ] } }
result = collection.insert_one(doc)
# result.inserted_id
pprint.pprint(list(collection.find()))
[ {'A': {'B': [{'C': {'Horiz': Decimal128('0.181665435'),
'Vert': Decimal128('0.178799435')}}]},
'_id': ObjectId('5ea79adb915cbf3c46f5d4ae')} ]
备注:
- PyMongo 的 decimal128 - Support for BSON Decimal128
- Python的十进制
模块
提供对十进制浮点运算的支持。
来自 PyMongo 的 decimal128 文档:
To ensure the result of a calculation can always be stored as BSON
Decimal128 use the context returned by create_decimal128_context()
(NOTE: as shown in the example code above).
我建议只添加一个编解码器以在插入时自动转换数据类型。如果您以递归方式更改数据类型以使用 Decimal128 对象,您可能会破坏与现有代码的兼容性。
您可以按照教程在 pymongo 文档中创建一个简单的 decimal.Decimal 编解码器 here
之后试图insert_one变成了collection。 我收到此错误:
bson.errors.InvalidDocument: cannot encode object: Decimal('0.16020'), of type: <class 'decimal.Decimal'>
当 JSON 不包含 decimal.Decimal
object 时代码运行良好。如果有解决方案,您能否考虑以递归方式对其进行编码,以使整个 python 字典 json_dic
兼容插入 MongoDB (因为有不止一次的实例json.dic
条目中的 class decimal.Decimal)。
如有任何帮助,我们将不胜感激!
编辑 1: 这是我正在处理的 json
import simplejson as json
from pymongo import MongoClient
json_string = '{"A" : {"B" : [{"C" : {"Horz" : 0.181665435,"Vert" : 0.178799435}}]}}'
json_dict = json.loads(json_string)
this_collection.insert_one(json_dict)
这会产生
bson.errors.InvalidDocument: cannot encode object: Decimal('0.181665435'), of type: <class 'decimal.Decimal'>
编辑 2: 不幸的是,我上面的例子简化了我现有的 JSON 和@
AttributeError: 'decimal.Decimal' object has no attribute 'items'
我的实际 Json,所以我在这里提供完整的 Json 希望找出问题所在(也 as a screen-shot):
json_string =
'
{
"Setting" : {
"GridOptions" : {
"Student" : "HighSchool",
"Lesson" : 1,
"Attended" : true
},
"Grades" : [
80,
50.75
],
"Count" : 2,
"Check" : "Coursework",
"Passed" : true
},
"Slides" : [
{
"Type" : "ABC",
"Duration" : 1.5
},
{
"Type" : "DEF",
"Duration" : 0.5
}
],
"Work" : {
"Class" : [
{
"Time" : 123456789,
"Marks" : {
"A" : 50,
"B" : 100
}
}
],
"CourseWorkDetail" : [
{
"Test" : {
"Mark" : 0.987654321
},
"ReadingDate" : "Feb162006",
"Reading" : 300.001,
"Values" : [
[
0.98765
],
[
-0.98765
]
]
},
{
"Test" : {
"Mark" : 0.123456789
},
"ReadingDate" : "Jan052010",
"Reading" : 200.005,
"Values" : [
[
0.12345
],
[
-0.12345
]
]
}
]
},
"listing" : 5
}
'
编辑 3: 作为对下面答案的补充,您可以像这样在字典中递归迭代并使用答案
中的函数def iterdict(dict_items, debug_out):
for k, v in dict_items.items():
if isinstance(v):
iterdict(v)
else:
dict_items[k] = convert_decimal(v)
return dict_items
Pymongo 无法识别 Decimal
- 这就是您收到错误的原因。
正确的 pymongo 插入是 coll.insert_one({"number1": Decimal128('8.916')})
。
您还需要导入 - from bson import Decimal128
现在,如果您想在不将 Decimal
更改为 Decimal128 的情况下处理 JSON 文件,您可以修改导入语句。
from bson import Decimal128 as Decimal
coll.insert_one({"number1": Decimal('8.916')})
编辑:
convert_decimal()
函数将在复杂的字典结构中执行 Decimal 到 Decimal128 的转换:
import simplejson as json
from pymongo import MongoClient
from decimal import Decimal
from bson.decimal128 import Decimal128
def convert_decimal(dict_item):
# This function iterates a dictionary looking for types of Decimal and converts them to Decimal128
# Embedded dictionaries and lists are called recursively.
if dict_item is None: return None
for k, v in list(dict_item.items()):
if isinstance(v, dict):
convert_decimal(v)
elif isinstance(v, list):
for l in v:
convert_decimal(l)
elif isinstance(v, Decimal):
dict_item[k] = Decimal128(str(v))
return dict_item
db = MongoClient()['mydatabase']
json_string = '{"A" : {"B" : [{"C" : {"Horz" : 0.181665435,"Vert" : 0.178799435}}]}}'
json_dict = json.loads(json_string, use_decimal=True)
db.this_collection.insert_one(convert_decimal(json_dict))
print(db.this_collection.find_one())
给出:
{'_id': ObjectId('5ea743aa297c9ccd52d33e05'), 'A': {'B': [{'C': {'Horz': Decimal128('0.181665435'), 'Vert': Decimal128('0.178799435')}}]}}
原创:
要将小数转换为 MongoDB 满意的 Decimal128,请将其转换为字符串,然后再转换为 Decimal128。此代码段可能会有所帮助:
from pymongo import MongoClient
from decimal import Decimal
from bson.decimal128 import Decimal128
db = MongoClient()['mydatabase']
your_number = Decimal('234.56')
your_number_128 = Decimal128(str(your_number))
db.mycollection.insert_one({'Number': your_number_128})
print(db.mycollection.find_one())
给出:
{'_id': ObjectId('5ea6ec9b52619c7b39b851cb'), 'Number': Decimal128('234.56')}
from bson.decimal128 import Decimal128, create_decimal128_context
from decimal import localcontext
decimal128_ctx = create_decimal128_context()
with localcontext(decimal128_ctx) as ctx:
horiz_val = Decimal128(ctx.create_decimal("0.181665435"))
vert_val = Decimal128(ctx.create_decimal("0.178799435"))
doc = { 'A': { 'B': [ { 'C': { 'Horiz': horiz_val, 'Vert': vert_val } } ] } }
result = collection.insert_one(doc)
# result.inserted_id
pprint.pprint(list(collection.find()))
[ {'A': {'B': [{'C': {'Horiz': Decimal128('0.181665435'),
'Vert': Decimal128('0.178799435')}}]},
'_id': ObjectId('5ea79adb915cbf3c46f5d4ae')} ]
备注:
- PyMongo 的 decimal128 - Support for BSON Decimal128
- Python的十进制 模块 提供对十进制浮点运算的支持。
来自 PyMongo 的 decimal128 文档:
To ensure the result of a calculation can always be stored as BSON Decimal128 use the context returned by
create_decimal128_context()
(NOTE: as shown in the example code above).
我建议只添加一个编解码器以在插入时自动转换数据类型。如果您以递归方式更改数据类型以使用 Decimal128 对象,您可能会破坏与现有代码的兼容性。
您可以按照教程在 pymongo 文档中创建一个简单的 decimal.Decimal 编解码器 here