Files
go-cart-actor/pkg/cart/cart_item_json.go
T
mats 60722e3414
Build and Publish / BuildAndDeployArm64 (push) Failing after 49s
Build and Publish / BuildAndDeployAmd64 (push) Has been cancelled
fixes
2026-06-16 13:14:35 +02:00

78 lines
1.9 KiB
Go

package cart
import (
"encoding/json"
"reflect"
"strings"
)
// cartItemAlias mirrors CartItem but has no custom (Un)MarshalJSON, so it is
// used to (de)serialize the typed fields without recursing.
type cartItemAlias CartItem
// knownItemKeys is the set of top-level JSON object keys owned by typed
// CartItem fields. Any other key in an item object is dynamic and kept in
// CartItem.Extra.
var knownItemKeys = buildKnownItemKeys()
func buildKnownItemKeys() map[string]struct{} {
keys := make(map[string]struct{})
t := reflect.TypeOf(CartItem{})
for i := 0; i < t.NumField(); i++ {
name, _, _ := strings.Cut(t.Field(i).Tag.Get("json"), ",")
if name == "" {
name = t.Field(i).Name // untagged fields serialize under their Go name
}
if name == "-" {
continue // e.g. Extra itself
}
keys[name] = struct{}{}
}
return keys
}
// MarshalJSON emits the typed fields and flattens Extra onto the same object.
// Typed fields take precedence on key collisions.
func (c CartItem) MarshalJSON() ([]byte, error) {
core, err := json.Marshal(cartItemAlias(c))
if err != nil || len(c.Extra) == 0 {
return core, err
}
merged := make(map[string]json.RawMessage, len(c.Extra)+8)
if err := json.Unmarshal(core, &merged); err != nil {
return nil, err
}
for k, v := range c.Extra {
if _, taken := merged[k]; taken {
continue
}
merged[k] = v
}
return json.Marshal(merged)
}
// UnmarshalJSON reads the typed fields and collects every other key into Extra.
func (c *CartItem) UnmarshalJSON(data []byte) error {
var alias cartItemAlias
if err := json.Unmarshal(data, &alias); err != nil {
return err
}
*c = CartItem(alias)
all := make(map[string]json.RawMessage)
if err := json.Unmarshal(data, &all); err != nil {
return err
}
for k := range all {
if _, known := knownItemKeys[k]; known {
delete(all, k)
}
}
if len(all) > 0 {
c.Extra = all
} else {
c.Extra = nil
}
return nil
}