英文:
Unmarshal JSON date string to BSON date in Golang
问题
这应该很简单,但我没有取得太多进展。假设我有一个包含UTC日期的JSON,像这样:
{
"name": "Lex",
"dob": "2022-11-01T06:30:30.639326208Z"
}
我想将其插入MongoDB集合。在Go中,我这样做:
import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
...
var doc any
// 顺便问一下,第二个参数是做什么的?文档中没有提到
_ = bson.UnmarshalExtJSON(jsonBytes, false, &doc)
mongoCollection.InsertOne(context.TODO(), doc)
插入操作是成功的,但dob
字段只是一个字符串。如何正确地将其作为日期插入?我的约束条件是:
- 我不能更改输入的JSON。
- 我不能手动编写Mongo文档,因为我可能处理的是大型不可预测的对象,而且不知道哪些字段/嵌套字段具有日期字符串。
这可行吗?似乎应该有一条明确的途径来实现这个目标。
英文:
This should be easy but I'm not making much headway. Say I have JSON with a UTC date like this:
{
"name": "Lex",
"dob": "2022-11-01T06:30:30.639326208Z"
}
I'd like to insert it in a MongoDB collection. In Go, I do:
import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
...
var doc any
// by the way what does the 2nd param do? docs don't mention it
_ = bson.UnmarshalExtJSON(jsonBytes, false, &doc)
mongoCollection.InsertOne(context.TODO(), doc)
The insert works, but the dob
field is just a string. What's the correct way to insert it as Date? My constraints are:
- I cannot change the input JSON
- I can't manually write the Mongo doc because I could be dealing with large unpredictable objects and won't know which fields/nested fields have a date string
Is this doable? Seems like there has to be a paved road for how to do this.
答案1
得分: 2
你可以定义一个自定义的bsoncodec.ValueDecoder
解码器来将日期字符串解码为time.Time
,然后通过RegisterTypeDecoder
进行注册。
func dateTimeDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
ts, _ := vr.ReadString()
t, err := time.Parse(time.RFC3339Nano, ts)
if err != nil {
return err
}
val.Set(reflect.ValueOf(t))
return nil
}
var tDateTime = reflect.TypeOf(time.Time{})
type TestDoc struct {
Name string `bson:"name"`
Dob time.Time `bson:"dob"`
}
func createCustomRegistry() *bsoncodec.RegistryBuilder {
var primitiveCodecs bson.PrimitiveCodecs
rb := bsoncodec.NewRegistryBuilder()
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
rb.RegisterTypeDecoder(tDateTime, bsoncodec.ValueDecoderFunc(dateTimeDecodeValue))
primitiveCodecs.RegisterPrimitiveCodecs(rb)
return rb
}
或者将日期字符串解析为BSON日期的示例代码
func dateTimeDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
ts, _ := vr.ReadString()
t, err := time.Parse(time.RFC3339Nano, ts)
if err != nil {
val.SetString(ts)
} else {
val.Set(reflect.ValueOf(primitive.DateTime(t.UnixMilli())))
}
return nil
}
var tDateTime = reflect.TypeOf(primitive.DateTime(0))
type TestDoc struct {
Name string `bson:"name"`
Dob primitive.DateTime `bson:"dob"`
}
func createCustomRegistry() *bsoncodec.RegistryBuilder {
var primitiveCodecs bson.PrimitiveCodecs
rb := bsoncodec.NewRegistryBuilder()
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
rb.RegisterTypeDecoder(tDateTime, bsoncodec.ValueDecoderFunc(dateTimeDecodeValue))
primitiveCodecs.RegisterPrimitiveCodecs(rb)
return rb
}
然后通过UnmarshalExtJSONWithRegistry
使用这个注册器。
func main() {
jsonBytes := []byte(`{
"name": "ben",
"dob": "2022-11-01T06:30:30.639326208Z"
}`)
var customRegistry = createCustomRegistry().Build()
var doc TestDoc
_ = bson.UnmarshalExtJSONWithRegistry(customRegistry, jsonBytes, false, &doc)
fmt.Println(doc)
}
完整代码请参考PLAYGROUND。
关于Canonical
参数,请参考文档。
Canonical Extended JSON是基于JSON标准的字符串格式,用于描述BSON文档。Canonical Extended JSON强调类型保留,但牺牲了可读性和互操作性。
英文:
You could define one custom decoders of bsoncodec.ValueDecoder
to decode the date string to time.Time
, then register it through RegisterTypeDecoder
func dateTimeDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
ts, _ := vr.ReadString()
t, err := time.Parse(time.RFC3339Nano, ts)
if err != nil {
return err
}
val.Set(reflect.ValueOf(t))
return nil
}
var tDateTime = reflect.TypeOf(time.Time{})
type TestDoc struct {
Name string `bson:"name"`
Dob time.Time `bson:"dob"`
}
func createCustomRegistry() *bsoncodec.RegistryBuilder {
var primitiveCodecs bson.PrimitiveCodecs
rb := bsoncodec.NewRegistryBuilder()
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
rb.RegisterTypeDecoder(tDateTime, bsoncodec.ValueDecoderFunc(dateTimeDecodeValue))
primitiveCodecs.RegisterPrimitiveCodecs(rb)
return rb
}
Or to parse date string to BSON date sample codes
func dateTimeDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
ts, _ := vr.ReadString()
t, err := time.Parse(time.RFC3339Nano, ts)
if err != nil {
val.SetString(ts)
} else {
val.Set(reflect.ValueOf(primitive.DateTime(t.UnixMilli())))
}
return nil
}
var tDateTime = reflect.TypeOf(primitive.DateTime(0))
type TestDoc struct {
Name string `bson:"name"`
Dob primitive.DateTime `bson:"dob"`
}
func createCustomRegistry() *bsoncodec.RegistryBuilder {
var primitiveCodecs bson.PrimitiveCodecs
rb := bsoncodec.NewRegistryBuilder()
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
rb.RegisterTypeDecoder(tDateTime, bsoncodec.ValueDecoderFunc(dateTimeDecodeValue))
primitiveCodecs.RegisterPrimitiveCodecs(rb)
return rb
}
Then use this register through UnmarshalExtJSONWithRegistry
func main() {
jsonBytes := []byte(`{
"name": "ben",
"dob": "2022-11-01T06:30:30.639326208Z"
}`)
var customRegistry = createCustomRegistry().Build()
var doc TestDoc
_ = bson.UnmarshalExtJSONWithRegistry(customRegistry, jsonBytes, false, &doc)
fmt.Println(doc)
}
Full codes
<kbd>PLAYGROUND</kbd>
As for Canonical
parameter please refer to doc
> Canonical Extended JSON - A string format based on the JSON standard that describes BSON documents. Canonical Extended JSON emphasizes type preservation at the expense of readability and interoperability.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论