bson.errors.InvalidDocument: 使用 json 时键 '$numberDecimal' 不能以 '$' 开头
bson.errors.InvalidDocument: key '$numberDecimal' must not start with '$' when using json
我有一个小的 json 文件,包含以下几行:
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33"
}
我的数据库集合中有一个模式,名为 test_dec。这就是我用来创建架构的内容:
db.createCollection("test_dec",
{validator: {
$jsonSchema: {
bsonType: "object",
required: ["IdTitulo","IdDirector"],
properties: {
IdTitulo: {
"bsonType": "string",
"description": "string type, nombre de la pelicula"
},
IdDirector: {
"bsonType": "string",
"description": "string type, nombre del director"
},
IdNumber : {
"bsonType": "int",
"description": "number type to test"
},
IdDecimal : {
"bsonType": "decimal",
"description": "decimal type"
}
}
}}
})
我多次尝试插入数据。问题出在 IdDecimal 字段值中。
一些试验,将 IdDecimal 行替换为:
"IdDecimal": 2.33
"IdDecimal": {"$numberDecimal": "2.33"}
"IdDecimal": NumberDecimal("2.33")
None 有效。第二个是 MongoDB 手册(mongodb-extended-json)提供的正式解决方案,错误是我在问题中的输出:bson.errors.InvalidDocument:键“$numberDecimal”不能以“$”开头。
我目前正在使用 python 加载 json。我一直在研究这个文件:
import os,sys
import re
import io
import json
from pymongo import MongoClient
from bson.raw_bson import RawBSONDocument
from bson.json_util import CANONICAL_JSON_OPTIONS,dumps,loads
import bsonjs as bs
#connection
client = MongoClient('localhost',27018,document_class=RawBSONDocument)
db = client['myDB']
coll = db['test_dec']
other_col = db['free']
for fname in os.listdir('/mnt/win/load'):
num = re.findall("\d+", fname)
if num:
with io.open(fname, encoding="ISO-8859-1") as f:
doc_data = loads(dumps(f,json_options=CANONICAL_JSON_OPTIONS))
print(doc_data)
test = '{"idTitulo":"La pelicula","idRelease":2019}'
raw_bson = bs.loads(test)
load_raw = RawBSONDocument(raw_bson)
db.other_col.insert_one(load_raw)
client.close()
我正在使用 json 文件。如果我尝试解析像 Decimal128('2.33') 这样的东西,输出是 "ValueError: No JSON object could be decoded",因为我的 json 格式无效。
的结果
db.other_col.insert_one(load_raw)
就是插入了"test"的内容。
但是我不能将 doc_data 与 RawBSONDocument 一起使用,因为它是那样的。它说:
TypeError: unpack_from() argument 1 must be string or buffer, not list:
当我设法将 json 直接解析为 RawBSONDocument 时,我得到了所有垃圾,数据库中的记录看起来像这里的示例:
{
"_id" : ObjectId("5eb2920a34eea737626667c2"),
"0" : "{\n",
"1" : "\t\"IdTitulo\": \"Gremlins\",\n",
"2" : "\t\"IdDirector\": \"Joe Dante\",\n",
"3" : "\t\"IdNumber\": 6,\n",
"4" : "\"IdDate\": {\"$date\": \"2010-06-18T:00.12:00Z\"}\t\n",
"5" : "}\n"
}
看来要将扩展的json加载到MongoDB中并不是那么简单。加长版是因为我要用schema验证
Oleg 指出它是 numberDecimal 而不是我之前的 NumberDecimal。我已经修复了 json 文件,但没有任何改变。
已执行:
with io.open(fname, encoding="ISO-8859-1") as f:
doc_data = json.load(f)
coll.insert(doc_data)
和 json 文件:
{
"IdTitulo": "Gremlins",
"IdDirector": "Joe Dante",
"IdNumber": 6,
"IdDecimal": {"$numberDecimal": "3.45"}
}
带有类型信息的 JSON 称为 Extended JSON。按照示例,为您的数据构造扩展 json:
ext_json = '''
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": {"$numberDecimal":"2.33"}
}
'''
在 Python 中,使用 json_util 将扩展的 json 加载到 Python 字典中:
from bson.json_util import loads
doc = loads(ext_json)
print(doc)
# {u'IdTitulo': u'Jaws', u'IdDirector': u'Steven Spielberg', u'IdDecimal': Decimal128('2.33'), u'IdNumber': 8}
这个加载的结果有时被称为 "BSON document" 但它不是二进制的 BSON。 "BSON" 在这种情况下实际上意味着某些值不是 python 标准库类型。 "document" 部分基本上意味着对象是一个字典。
您会注意到 IdNumber 是非标准库类型:
print type(doc['IdDecimal'])
# <class 'bson.decimal128.Decimal128'>
要将此字典插入 MongoDB,请按照 pymongo tutorial:
from pymongo import MongoClient
client = MongoClient('localhost', 14420)
db = client.test_database
collection = db.test_collection
collection.insert_one(doc)
print(doc)
最后,我找到了解决方案,它使用的是 RawBSONDocument。
首先是 json 文件:
{
"IdTitulo": "Dead Snow",
"IdDirector": "Tommy Wirkola",
"IdNumber": 11,
"IdDecimal": {"$numberDecimal": "2.22"}
}
& 验证模式文件:
db.createCollection("test_dec",
{validator: {
$jsonSchema: {
bsonType: "object",
required: ["IdTitulo","IdDirector"],
properties: {
IdTitulo: {
"bsonType": "string",
"description": "string type, nombre de la pelicula"
},
IdDirector: {
"bsonType": "string",
"description": "string type, nombre del director"
},
IdNumber : {
"bsonType": "int",
"description": "number type to test"
},
IdDecimal : {
"bsonType": "decimal",
"description": "decimal type"
}
}
}}
})
因此,本例中的集合是"test_dec"。
和打开文件“.json”的python脚本,读取并解析它以导入到MongoDB.
import json
from bson.raw_bson import RawBSONDocument
from pymongo import MongoClient
import bsonjs
#connection
client = MongoClient('localhost',27018)
db = client['movieDB']
coll = db['test_dec']
#open an read file
with open('1.json', 'r') as jfile:
data = jfile.read()
loaded = json.loads(data)
dumped = json.dumps(loaded, indent=4)
bson_bytes = bsonjs.loads(dumped)
coll.insert_one(RawBSONDocument(bson_bytes))
client.close()
插入的文档:
{
"_id" : ObjectId("5eb971ec6fbab859dfae8a6f"),
"IdTitulo" : "Dead Snow",
"IdDirector" : "Toomy Wirkola",
"IdDecimal" : NumberDecimal("2.22"),
"IdNumber" : 11
}
我不知道它是如何翻转字段IdDecimal和IdNumber的,但是它通过了验证,我真的很高兴。
我尝试使用 'hello' 而不是 NumberDecimal 中的数字的文档,插入结果为:
{
"_id" : ObjectId("5eb973b76fbab859dfae8ecd"),
"IdTitulo" : "Shining",
"IdDirector" : "Stanley Kubrick",
"IdDecimal" : NumberDecimal("NaN"),
"IdNumber" : 19
}
感谢所有试图提供帮助的人。特别是奥列格!!!谢谢你这么有耐心。
你能不能只用bson.decimal128.Decimal128?还是我遗漏了什么?
from pymongo import MongoClient
from bson.decimal128 import Decimal128
db = MongoClient()['mydatabase']
data = {
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33"
}
data['IdDecimal'] = Decimal128(data['IdDecimal'])
db.other_col.insert_one(data)
我又掷了一个骰子。如果您按原样使用模式验证,我建议定义一个 class 并明确定义每个字段以及您建议如何将字段转换为相关的 python 数据类型。虽然您的解决方案是通用的,但数据结构必须严格以匹配验证。
IMO 这更清楚,您可以控制 class.
中的任何错误等
只是为了确认我 运行 模式验证,这适用于提供的验证。
from pymongo import MongoClient
import bson.json_util
import dateutil.parser
import json
class Film:
def __init__(self, file):
data = file.read()
loaded = json.loads(data)
self.IdTitulo = loaded.get('IdTitulo')
self.IdDirector = loaded.get('IdDirector')
self.IdDecimal = bson.json_util.Decimal128(loaded.get('IdDecimal'))
self.IdNumber = int(loaded.get('IdNumber'))
self.IdDateTime = dateutil.parser.parse(loaded.get('IdDateTime'))
def insert_one(self, collection):
collection.insert_one(self.__dict__)
client = MongoClient()
mycollection = client.mydatabase.test_dec
with open('c:/temp/1.json', 'r') as jfile:
film = Film(jfile)
film.insert_one(mycollection)
给出:
> db.test_dec.findOne()
{
"_id" : ObjectId("5eba79eabf951a15d32843ae"),
"IdTitulo" : "Jaws",
"IdDirector" : "Steven Spielberg",
"IdDecimal" : NumberDecimal("2.33"),
"IdNumber" : 8,
"IdDateTime" : ISODate("2020-05-12T10:08:21Z")
}
>
JSON 使用的文件:
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33",
"IdDateTime": "2020-05-12T11:08:21+0100"
}
我有一个小的 json 文件,包含以下几行:
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33"
}
我的数据库集合中有一个模式,名为 test_dec。这就是我用来创建架构的内容:
db.createCollection("test_dec",
{validator: {
$jsonSchema: {
bsonType: "object",
required: ["IdTitulo","IdDirector"],
properties: {
IdTitulo: {
"bsonType": "string",
"description": "string type, nombre de la pelicula"
},
IdDirector: {
"bsonType": "string",
"description": "string type, nombre del director"
},
IdNumber : {
"bsonType": "int",
"description": "number type to test"
},
IdDecimal : {
"bsonType": "decimal",
"description": "decimal type"
}
}
}}
})
我多次尝试插入数据。问题出在 IdDecimal 字段值中。
一些试验,将 IdDecimal 行替换为:
"IdDecimal": 2.33
"IdDecimal": {"$numberDecimal": "2.33"}
"IdDecimal": NumberDecimal("2.33")
None 有效。第二个是 MongoDB 手册(mongodb-extended-json)提供的正式解决方案,错误是我在问题中的输出:bson.errors.InvalidDocument:键“$numberDecimal”不能以“$”开头。
我目前正在使用 python 加载 json。我一直在研究这个文件:
import os,sys
import re
import io
import json
from pymongo import MongoClient
from bson.raw_bson import RawBSONDocument
from bson.json_util import CANONICAL_JSON_OPTIONS,dumps,loads
import bsonjs as bs
#connection
client = MongoClient('localhost',27018,document_class=RawBSONDocument)
db = client['myDB']
coll = db['test_dec']
other_col = db['free']
for fname in os.listdir('/mnt/win/load'):
num = re.findall("\d+", fname)
if num:
with io.open(fname, encoding="ISO-8859-1") as f:
doc_data = loads(dumps(f,json_options=CANONICAL_JSON_OPTIONS))
print(doc_data)
test = '{"idTitulo":"La pelicula","idRelease":2019}'
raw_bson = bs.loads(test)
load_raw = RawBSONDocument(raw_bson)
db.other_col.insert_one(load_raw)
client.close()
我正在使用 json 文件。如果我尝试解析像 Decimal128('2.33') 这样的东西,输出是 "ValueError: No JSON object could be decoded",因为我的 json 格式无效。
的结果 db.other_col.insert_one(load_raw)
就是插入了"test"的内容。 但是我不能将 doc_data 与 RawBSONDocument 一起使用,因为它是那样的。它说:
TypeError: unpack_from() argument 1 must be string or buffer, not list:
当我设法将 json 直接解析为 RawBSONDocument 时,我得到了所有垃圾,数据库中的记录看起来像这里的示例:
{
"_id" : ObjectId("5eb2920a34eea737626667c2"),
"0" : "{\n",
"1" : "\t\"IdTitulo\": \"Gremlins\",\n",
"2" : "\t\"IdDirector\": \"Joe Dante\",\n",
"3" : "\t\"IdNumber\": 6,\n",
"4" : "\"IdDate\": {\"$date\": \"2010-06-18T:00.12:00Z\"}\t\n",
"5" : "}\n"
}
看来要将扩展的json加载到MongoDB中并不是那么简单。加长版是因为我要用schema验证
Oleg 指出它是 numberDecimal 而不是我之前的 NumberDecimal。我已经修复了 json 文件,但没有任何改变。
已执行:
with io.open(fname, encoding="ISO-8859-1") as f:
doc_data = json.load(f)
coll.insert(doc_data)
和 json 文件:
{
"IdTitulo": "Gremlins",
"IdDirector": "Joe Dante",
"IdNumber": 6,
"IdDecimal": {"$numberDecimal": "3.45"}
}
JSON 称为 Extended JSON。按照示例,为您的数据构造扩展 json:
ext_json = '''
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": {"$numberDecimal":"2.33"}
}
'''
在 Python 中,使用 json_util 将扩展的 json 加载到 Python 字典中:
from bson.json_util import loads
doc = loads(ext_json)
print(doc)
# {u'IdTitulo': u'Jaws', u'IdDirector': u'Steven Spielberg', u'IdDecimal': Decimal128('2.33'), u'IdNumber': 8}
这个加载的结果有时被称为 "BSON document" 但它不是二进制的 BSON。 "BSON" 在这种情况下实际上意味着某些值不是 python 标准库类型。 "document" 部分基本上意味着对象是一个字典。
您会注意到 IdNumber 是非标准库类型:
print type(doc['IdDecimal'])
# <class 'bson.decimal128.Decimal128'>
要将此字典插入 MongoDB,请按照 pymongo tutorial:
from pymongo import MongoClient
client = MongoClient('localhost', 14420)
db = client.test_database
collection = db.test_collection
collection.insert_one(doc)
print(doc)
最后,我找到了解决方案,它使用的是 RawBSONDocument。
首先是 json 文件:
{
"IdTitulo": "Dead Snow",
"IdDirector": "Tommy Wirkola",
"IdNumber": 11,
"IdDecimal": {"$numberDecimal": "2.22"}
}
& 验证模式文件:
db.createCollection("test_dec",
{validator: {
$jsonSchema: {
bsonType: "object",
required: ["IdTitulo","IdDirector"],
properties: {
IdTitulo: {
"bsonType": "string",
"description": "string type, nombre de la pelicula"
},
IdDirector: {
"bsonType": "string",
"description": "string type, nombre del director"
},
IdNumber : {
"bsonType": "int",
"description": "number type to test"
},
IdDecimal : {
"bsonType": "decimal",
"description": "decimal type"
}
}
}}
})
因此,本例中的集合是"test_dec"。
和打开文件“.json”的python脚本,读取并解析它以导入到MongoDB.
import json
from bson.raw_bson import RawBSONDocument
from pymongo import MongoClient
import bsonjs
#connection
client = MongoClient('localhost',27018)
db = client['movieDB']
coll = db['test_dec']
#open an read file
with open('1.json', 'r') as jfile:
data = jfile.read()
loaded = json.loads(data)
dumped = json.dumps(loaded, indent=4)
bson_bytes = bsonjs.loads(dumped)
coll.insert_one(RawBSONDocument(bson_bytes))
client.close()
插入的文档:
{
"_id" : ObjectId("5eb971ec6fbab859dfae8a6f"),
"IdTitulo" : "Dead Snow",
"IdDirector" : "Toomy Wirkola",
"IdDecimal" : NumberDecimal("2.22"),
"IdNumber" : 11
}
我不知道它是如何翻转字段IdDecimal和IdNumber的,但是它通过了验证,我真的很高兴。
我尝试使用 'hello' 而不是 NumberDecimal 中的数字的文档,插入结果为:
{
"_id" : ObjectId("5eb973b76fbab859dfae8ecd"),
"IdTitulo" : "Shining",
"IdDirector" : "Stanley Kubrick",
"IdDecimal" : NumberDecimal("NaN"),
"IdNumber" : 19
}
感谢所有试图提供帮助的人。特别是奥列格!!!谢谢你这么有耐心。
你能不能只用bson.decimal128.Decimal128?还是我遗漏了什么?
from pymongo import MongoClient
from bson.decimal128 import Decimal128
db = MongoClient()['mydatabase']
data = {
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33"
}
data['IdDecimal'] = Decimal128(data['IdDecimal'])
db.other_col.insert_one(data)
我又掷了一个骰子。如果您按原样使用模式验证,我建议定义一个 class 并明确定义每个字段以及您建议如何将字段转换为相关的 python 数据类型。虽然您的解决方案是通用的,但数据结构必须严格以匹配验证。
IMO 这更清楚,您可以控制 class.
中的任何错误等只是为了确认我 运行 模式验证,这适用于提供的验证。
from pymongo import MongoClient
import bson.json_util
import dateutil.parser
import json
class Film:
def __init__(self, file):
data = file.read()
loaded = json.loads(data)
self.IdTitulo = loaded.get('IdTitulo')
self.IdDirector = loaded.get('IdDirector')
self.IdDecimal = bson.json_util.Decimal128(loaded.get('IdDecimal'))
self.IdNumber = int(loaded.get('IdNumber'))
self.IdDateTime = dateutil.parser.parse(loaded.get('IdDateTime'))
def insert_one(self, collection):
collection.insert_one(self.__dict__)
client = MongoClient()
mycollection = client.mydatabase.test_dec
with open('c:/temp/1.json', 'r') as jfile:
film = Film(jfile)
film.insert_one(mycollection)
给出:
> db.test_dec.findOne()
{
"_id" : ObjectId("5eba79eabf951a15d32843ae"),
"IdTitulo" : "Jaws",
"IdDirector" : "Steven Spielberg",
"IdDecimal" : NumberDecimal("2.33"),
"IdNumber" : 8,
"IdDateTime" : ISODate("2020-05-12T10:08:21Z")
}
>
JSON 使用的文件:
{
"IdTitulo": "Jaws",
"IdDirector": "Steven Spielberg",
"IdNumber": 8,
"IdDecimal": "2.33",
"IdDateTime": "2020-05-12T11:08:21+0100"
}