Pymongo:无法对decimal.Decimal类型的对象进行编码?

Moh*_*mad 5 python json decimal mongodb pymongo

尝试将insert_one插入集合后。我收到此错误:

bson.errors.InvalidDocument: cannot encode object: Decimal('0.16020'), of type: <class 'decimal.Decimal'>
Run Code Online (Sandbox Code Playgroud)

当 JSON 不包含该对象时,代码运行良好decimal.Decimal。如果有一个解决方案,您可以考虑以递归方式对其进行编码,以使整个 python 字典json_dic兼容插入到 MongoDB 中(因为条目中存在不止一次 Decimal.Decimal 类的实例json.dic)。

编辑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)
Run Code Online (Sandbox Code Playgroud)

这会产生 bson.errors.InvalidDocument: cannot encode object: Decimal('0.181665435'), of type: <class 'decimal.Decimal'>

编辑2:不幸的是,我上面的例子过于简化了我现有的JSON,并且@ Belly Buster提供的答案(尽管与上面的Json一起工作正常)抛出了一个错误:

AttributeError: 'decimal.Decimal' object has no attribute 'items'

与我的实际 JSON,所以我在这里提供完整的 JSON,希望找出问题所在(也作为屏幕截图):

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
}
'
Run Code Online (Sandbox Code Playgroud)

编辑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
Run Code Online (Sandbox Code Playgroud)

Bel*_*ter 7

编辑:

convert_decimal()函数将在复杂的 dict 结构中执行 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())
Run Code Online (Sandbox Code Playgroud)

给出:

{'_id': ObjectId('5ea743aa297c9ccd52d33e05'), 'A': {'B': [{'C': {'Horz': Decimal128('0.181665435'), 'Vert': Decimal128('0.178799435')}}]}}
Run Code Online (Sandbox Code Playgroud)

原来的:

要将小数转换为 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())
Run Code Online (Sandbox Code Playgroud)

给出:

{'_id': ObjectId('5ea6ec9b52619c7b39b851cb'), 'Number': Decimal128('234.56')}
Run Code Online (Sandbox Code Playgroud)