英文:
Skip nulls when unmarshalling JSON array into slice of pointers
问题
我有以下结构体:
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products []*Product `json:"products"`
}
func (i *Item) Transform(input []byte) error {
return json.Unmarshal(input, i)
}
我需要对Products
及其成员以及嵌套成员(例如[]*Variant{}
或[]*Shipping{}
等)执行多个操作。
因为Item
结构体中的大多数切片都是指针切片,所以我处理这些数据的代码如下所示:
for _, product := range i.Products {
if product == nil {
continue
}
for _, variant := range product.Variants {
if variant == nil {
continue
}
for _, shipping := range shippings {
if shipping == nil {
continue
}
// 其他操作...
}
}
}
有没有办法在指针切片的nil
值上模拟omitempty
的效果?以下是示例。
JSON输入:
{
"products": [
null,
{},
null
]
}
输出,等效于:
input := Item{
Products: []Product{ {} }, // 没有null值
}
我尝试在[]*Property
上使用omitempty
,但它不起作用。我还尝试使用非指针值,但Go会将每个null初始化为默认的结构体值。
英文:
I have following struct:
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products []*Product `json:"products"`
}
func (i *Item) Transform(input []byte) error {
return json.Unmarshal(input, i)
}
I have to perform several operations on Products
and it's members and it's nested members as well which for instance are []*Variant{}
or []*Shipping{}
etc.
Because most of slices in Item
struct are slice of pointers, my code to process this data looks like this:
for _, product := range i.Products {
if product == nil {
continue
}
for _, variant := range product.Variants {
if variant == nil {
continue
}
for _, shipping := range shippings {
if shipping == nil {
continue
}
// and so on...
}
}
}
Is there any way to mimick omitempty
on nil
values in slice of pointers? Example below.
JSON input:
{
"products": [
null,
{},
null
]
}
output, equivalent to:
input := Item{
Products: []Product{ {} }, // without nulls
}
I tried to use omitempty
on []*Property
but it doesn't work. I also tried to use non-pointer values but then Go initialises every null to default struct value.
答案1
得分: 3
你可以实现一个自定义的json.Unmarshaler
。
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products ProductList `json:"products"`
}
// 如果你打算修改切片中的各个元素,请使用 []*Product。
// 如果元素是只读的,请使用 []Product。
type ProductList []*Product
// 实现json.Unmarshaler接口。
// 这将导致编码/解码器在遇到ProductList实例时调用UnmarshalJSON方法,
// 而不是执行默认的解码操作。
func (ls *ProductList) UnmarshalJSON(data []byte) error {
// 首先,进行正常的解码
pp := []*Product{}
if err := json.Unmarshal(data, &pp); err != nil {
return err
}
// 然后,只附加非nil值
for _, p := range pp {
if p != nil {
*ls = append(*ls, p)
}
}
// 完成
return nil
}
感谢@blackgreen:
在Go1.18及更高版本中,你不需要为其他[]*Variant{}
和[]*Shipping{}
类型实现自定义的解码操作。相反,你可以使用具有元素类型参数的切片类型。
type SkipNullList[T any] []*T
func (ls *SkipNullList[T]) UnmarshalJSON(data []byte) error {
pp := []*T{}
if err := json.Unmarshal(data, &pp); err != nil {
return err
}
for _, p := range pp {
if p != nil {
*ls = append(*ls, p)
}
}
return nil
}
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products SkipNullList[Product] `json:"products"`
}
type Product struct {
// ...
Variants SkipNullList[Variant] `json:"variants"`
}
type Variant struct {
// ...
Shippings SkipNullList[Shipping] `json:"shippings"`
}
https://go.dev/play/p/az_9Mb_RBKX
英文:
You could implement a custom json.Unmarshaler
.
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products ProductList `json:"products"`
}
// Use []*Product if you intend to modify
// the individual elements in the slice.
// Use []Product if the elements are read-only.
type ProductList []*Product
// Implememt the json.Unmarshaler interface.
// This will cause the encoding/json decoder to
// invoke the UnmarshalJSON method, instead of
// performing the default decoding, whenever it
// encounters a ProductList instance.
func (ls *ProductList) UnmarshalJSON(data []byte) error {
// first, do a normal unmarshal
pp := []*Product{}
if err := json.Unmarshal(data, &pp); err != nil {
return err
}
// next, append only the non-nil values
for _, p := range pp {
if p != nil {
*ls = append(*ls, p)
}
}
// done
return nil
}
Credit to @blackgreen:
With Go1.18 and up, you don't have to implement the custom unmarshaling for the other []*Variant{}
and []*Shipping{}
types. Instead you can use a slice type with a type parameter for the element.
type SkipNullList[T any] []*T
func (ls *SkipNullList[T]) UnmarshalJSON(data []byte) error {
pp := []*T{}
if err := json.Unmarshal(data, &pp); err != nil {
return err
}
for _, p := range pp {
if p != nil {
*ls = append(*ls, p)
}
}
return nil
}
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Products SkipNullList[Product] `json:"products"`
}
type Product struct {
// ...
Variants SkipNullList[Variant] `json:"variants"`
}
type Variant struct {
// ...
Shippings SkipNullList[Shipping] `json:"shippings"`
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论