将 JSON 数组解组为指针切片时跳过空值
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
}
// and so on...
}
}
}
有什么方法可以在指针切片中的 nil
值上模仿 omitempty
吗?示例如下。
JSON 输入:
{
"products": [
null,
{},
null
]
}
输出,相当于:
input := Item{
Products: []Product{ {} }, // without nulls
}
我尝试在 []*Property
上使用 omitempty
,但它不起作用。我还尝试使用非指针值,但随后 Go 将每个 null 初始化为默认结构值。
您可以实施自定义 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
}
归功于@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"`
}
我有以下结构:
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
}
// and so on...
}
}
}
有什么方法可以在指针切片中的 nil
值上模仿 omitempty
吗?示例如下。
JSON 输入:
{
"products": [
null,
{},
null
]
}
输出,相当于:
input := Item{
Products: []Product{ {} }, // without nulls
}
我尝试在 []*Property
上使用 omitempty
,但它不起作用。我还尝试使用非指针值,但随后 Go 将每个 null 初始化为默认结构值。
您可以实施自定义 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
}
归功于@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"`
}