Go使用bigquery.NullTimestamp插入BigQuery时报错:字段非记录
问题:BigQuery插入时报错"This field: modified_at is not a record"
API返回的日期字段格式不统一(比如dd-mm-yyyy),我想把这些日期统一转成BigQuery的TIMESTAMP类型。于是把原本的time.Time换成bigquery.NullTimestamp,定义了自定义类型StringDutchDateToTimestamp并实现了UnmarshalJSON方法,但插入数据时一直报错:
错误信息
failed with error: {Location: "modified_at"; Message: "This field: modified_at is not a record."; Reason: "invalid"}
相关代码
type StringDutchDateToTimestamp bigquery.NullTimestamp type ArticleData struct { ModifiedAt StringDutchDateToTimestamp `json:"modified_at" bigquery:"modified_at"` Barcode string `json:"barcode" bigquery:"barcode"` PromoStartdate StringDutchDateToTimestamp `json:"promo_startdate,omitempty" bigquery:"promo_startdate"` PromoEnddate StringDutchDateToTimestamp `json:"promo_enddate,omitempty" bigquery:"promo_enddate"` } func (t *StringDutchDateToTimestamp) UnmarshalJSON(b []byte) error { if string(b) == "\"\"" || string(b) == "null" { t.Valid = false t.Timestamp = time.Time{} // Set Timestamp to a zero time value return nil } var dt string if err := json.Unmarshal(b, &dt); err != nil { return err } ts, err := time.Parse("02-01-2006", dt) if err != nil { return err } t.Timestamp = ts t.Valid = true return nil } // 插入数据的代码片段 inserter := b.Dataset.Table(b.ArticleData).Inserter() if err := inserter.Put(context.Background(), articles); err != nil { return fmt.Errorf("articledata.inserter.put: %w", err) }
输入数据
{ "modified_at": "28-06-2023", "barcode": "1111111180588", "promo_startdate": null, "promo_enddate": null }
spew.Dump()输出
(models.ArticleData) { ModifiedAt: (models.StringDutchDateToTimestamp) { Timestamp: (time.Time) 2023-06-28 00:00:00 +0000 UTC, Valid: (bool) true }, Barcode: (string) (len=13) "1111111180588", PromoStartdate: (models.StringDutchDateToTimestamp) { Timestamp: (time.Time) 0001-01-01 00:00:00 +0000 UTC, Valid: (bool) false }, PromoEnddate: (models.StringDutchDateToTimestamp) { Timestamp: (time.Time) 0001-01-01 00:00:00 +0000 UTC, Valid: (bool) false } }
BigQuery表结构
modified_at NULLABLE TIMESTAMP barcode NULLABLE STRING promo_startdate NULLABLE TIMESTAMP promo_enddate NULLABLE TIMESTAMP
问题原因及解决方法
问题出在自定义类型没有实现BigQuery的类型转换接口。你用类型别名type StringDutchDateToTimestamp bigquery.NullTimestamp定义的自定义类型,不会自动继承原类型的方法,BigQuery客户端无法识别它是TIMESTAMP类型,反而会把它当成包含Timestamp和Valid字段的结构体(也就是错误里说的"record")。
解决步骤:
- 最简单的方式是用结构体包装
bigquery.NullTimestamp,而非类型别名,这样可以复用原类型的所有方法,同时保留自定义的JSON反序列化逻辑:
type StringDutchDateToTimestamp struct { bigquery.NullTimestamp } func (t *StringDutchDateToTimestamp) UnmarshalJSON(b []byte) error { if string(b) == "\"\"" || string(b) == "null" { t.Valid = false t.Timestamp = time.Time{} return nil } var dt string if err := json.Unmarshal(b, &dt); err != nil { return err } ts, err := time.Parse("02-01-2006", dt) if err != nil { return err } t.Timestamp = ts t.Valid = true return nil }
- 也可以手动实现
driver.Valuer接口,明确告诉BigQuery客户端如何转换这个类型:
func (t StringDutchDateToTimestamp) Value() (driver.Value, error) { if !t.Valid { return nil, nil } return t.Timestamp, nil }
两种方式都能解决问题,推荐第一种结构体包装的方式,更简洁且能复用原类型的所有逻辑。
内容的提问来源于stack exchange,提问作者RemcoE33
相关产品推荐
相关产品推荐

