So, I have struct P. I need to unmarshal some json data into P but sometimes it comes embedded struct, Embedded. In either case, I unmarshal the json from the API and need to format the "Formatted" field. It seems in the Embedded case my unmarshaller doesn't get called.
I have the following code:
package main
import (
"encoding/json"
"fmt"
)
type P struct {
Name string `json:"name"`
Formatted string `json:"formatted"`
}
type Embedded struct {
A struct {
B struct {
*P
} `json:"b"`
} `json:"a"`
}
func (p *P) UnmarshalJSON(b []byte) error {
type Alias P
a := &struct {
*Alias
}{
Alias: (*Alias)(p),
}
if err := json.Unmarshal(b, &a); err != nil {
return err
}
a.Formatted = fmt.Sprintf("Hi, my name is %v", a.Name)
return nil
}
func simple() {
b := []byte(`{"name":"bob"}`)
p := &P{}
if err := json.Unmarshal(b, &p); err != nil {
panic(err)
}
fmt.Printf("normal: %+v\n", p)
}
func embedded() {
b := []byte(`{"a":{"b":{"name":"bob"}}}`)
e := &Embedded{}
if err := json.Unmarshal(b, &e); err != nil {
panic(err)
}
fmt.Printf("embedded: %+v\n", e.A.B.P)
}
func main() {
simple()
embedded()
}
(I realize I can get rid of the custom unmarshaller and create a method to format the name but wanted to see if this way was possible.)
I don't know enough to explain all the reasons, I will just list what works and what doesn't. Someone more knowledgeable can fill you in on the reasons behind it.
The following works when B is a *struct, not sure why.
type Embedded struct {
A struct {
B *struct {
P
} `json:"b"`
} `json:"a"`
}
The following also works. I'm guessing that using an anonymous struct had some effect in the last one since a *struct is not required here.
type embedP struct {
P
}
type Embedded struct {
A struct {
B embedP `json:"b"`
} `json:"a"`
}
The following works if *P is initialised.
type embedP struct {
*P
}
type intermediate struct {
B embedP `json:"b"`
}
type Embedded struct {
A intermediate `json:"a"`
}
e := &Embedded{A:intermediate{embedP{P:&P{}}}}
But the same thing doesn't work with anonymous structs.
type Embedded struct {
A struct {
B struct {
*P
} `json:"b"`
} `json:"a"`
}
e := &Embedded{A : struct{B struct{*P}`json:"b"`}{B: struct{*P}{&P{}}}}
Play link
Other improvements
If p := &P{} is already a pointer you don't need to pass &p in json.Unmarshal. json.Unmarshal(b, p) would suffice. Same with e := &Embedded{}.
To extent #John's answer, take a look at the source code of json decoder, especially method indirect(v reflect.Value, decodingNull bool) line 442-483.
// indirect walks down v allocating pointers as needed,
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
The method returns, json.Unmarshaler, encoding.TextUnmarshaler and the value of v. In current implementation, inside the method, basically the following steps were executed
If argument v is not a pointer, it will return immediately without checking whether v implements json.Unmarshaler/encoding.TextUnmarshaler or not. The method assigns nil for both unmarshaller regardless B implements custom unmarshaller or not.
If argument v is a pointer, it will check whether v implements json.Unmarshaler/encoding.TextUnmarshaler or not. In this case, if v is nil, a new value will be assigned to v.
If Embedded is defined as
type Embedded struct {
A struct {
B struct {
*P
} `json:"b"`
} `json:"a"`
}
when, decoding "b":{"name":"bob"} to field B, since B is not a pointer, (1) is applicable. As the result, custom unmarshaller is returned as nil, thus never being called. The json decoder uses default unmarshaller to decode json value to B's fields.
If Embedded is defined as
type Embedded struct {
A struct {
*B struct {
P
} `json:"b"`
} `json:"a"`
}
since field B is a pointer, (2) is applicable. The decoder allocates new struct{*P} to B, detects that B implements custom unmarshaller, then call it as expected. The following declaration
type Embedded struct {
A struct {
*B struct {
*P
} `json:"b"`
} `json:"a"`
}
also works, if P is preallocated, i.e.
//...
e := Embedded{}
e.A.B = &struct{ *P }{P: &P{}}
//...
If it's not preallocated, in (2) the decoder will assign &struct{*P}{} to B, then call the custom unmarshaller with B.P == nil. As the result, json value can't be captured by B.P during unmarshall.
Note:
I'm not sure whether it is desired behavior or not, and I can't find a clear documentation related to embedded struct.
Related
I'm doing some experimentation with type parameters to come up with a generic way of wiring up structs that generate a response to JSON HTTP requests.
The Method interface which the structs must implement has a SetParams method. This will work as expected as long as the implementation uses a pointer receiver.
My question: Is there any way of making this a compile time error if SetParams has a value receiver?
Here is an example demonstrating the problem with a SetParams that has a value receiver:
package main
import (
"encoding/json"
"fmt"
"log"
)
type PingParams struct {
Name string
}
type PingResponse struct {
Message string
}
func (p PingParams) Greeting() string {
if p.Name != "" {
return fmt.Sprintf("Hello, %s", p.Name)
}
return fmt.Sprintf("Hello, nobody!")
}
type GoodPing struct {
Params PingParams
}
// SetParams has a pointer receiver.
func (m *GoodPing) SetParams(p PingParams) {
fmt.Printf("assign %v with pointer receiver, Good!\n", p)
m.Params = p
}
func (m GoodPing) Run() (*PingResponse, error) {
return &PingResponse{Message: fmt.Sprintf("%T %s", m, m.Params.Greeting())}, nil
}
type BadPing struct {
Params PingParams
}
// SetParams has a value receiver.
func (m BadPing) SetParams(p PingParams) {
fmt.Printf("assign %v with value receiver, Bad!\n", p)
m.Params = p
}
func (m BadPing) Run() (*PingResponse, error) {
return &PingResponse{Message: fmt.Sprintf("%T %s", m, m.Params.Greeting())}, nil
}
type Method[M, RQ, RS any] interface {
// Run builds the RPC result.
Run() (*RS, error)
// SetParams is intended to set the request parameters in the struct implementing the RPC method.
// This then allows the request parameters to be easily available to all methods of the Method struct.
// The method MUST have a pointer receiver. This is NOT enforced at compile time.
SetParams(p RQ)
// The following line requires the implementing type is a pointer to M.
*M
// https://stackoverflow.com/a/72090807
}
func HandlerMethod[M, RQ, RS any, T Method[M, RQ, RS]](in json.RawMessage) (*RS, error) {
// A real implementation of this would return a func for wiring into a request router
var req RQ
err := json.Unmarshal(in, &req)
if err != nil {
return nil, err
}
var m T = new(M)
m.SetParams(req)
return m.Run()
}
func main() {
payload := []byte(`{"Name": "Mark"}`)
bad, err := HandlerMethod[BadPing, PingParams, PingResponse](payload)
if err != nil {
log.Fatal(err)
}
fmt.Println(bad.Message)
good, err := HandlerMethod[GoodPing, PingParams, PingResponse](payload)
if err != nil {
log.Fatal(err)
}
fmt.Println(good.Message)
}
https://go.dev/play/p/Eii8ADkmDxE
You can't do that.
When in your code you do this:
var m T = new(M)
even if T's type set includes only *M as a type term, *M's method set includes methods declared on M. The compiler can't check for you how the method ends up in *M's method set.
It is your responsibility when declaring the method SetParam on BadPing to make sure that the method doesn't attempt to unfruitfully modify the receiver.
Golang - Customer Unmarshaler/Marshaler on pointer with nil/null value
I'm trying to implement custom UnmarshalJSON and MarshalJSON on pointer type, however none of this function is called when data from json is null/nil like in example below:
package main
import (
"encoding/json"
"fmt"
)
type A struct {
B *B `json:"b,omitempty"`
}
type B int
// Only for displaying value instead of
// pointer address when calling `fmt.Println`
func (b *B) String() string {
if b == nil {
return "nil"
}
return fmt.Sprintf("%d", *b)
}
// This function is not triggered when json
// data contains null instead of number value
func (b *B) UnmarshalJSON(data []byte) error {
fmt.Println("UnmarshalJSON on B called")
var value int
if err := json.Unmarshal(data, &value); err != nil {
return err
}
if value == 7 {
*b = B(3)
}
return nil
}
// This function is not triggered when `B`
// is pointer type and has `nil` value
func (b *B) MarshalJSON() ([]byte, error) {
fmt.Println("MarshalJSON on B called")
if b == nil {
return json.Marshal(0)
}
if *b == 3 {
return json.Marshal(7)
}
return json.Marshal(*b)
}
func main() {
var a A
// this won't call `UnmarshalJSON`
json.Unmarshal([]byte(`{ "b": null }`), &a)
fmt.Printf("a: %+v\n", a)
// this won't call `MarshalJSON`
b, _ := json.Marshal(a)
fmt.Printf("b: %s\n\n", string(b))
// this would call `UnmarshalJSON`
json.Unmarshal([]byte(`{ "b": 7 }`), &a)
fmt.Printf("a: %+v\n", a)
// this would call `MarshalJSON`
b, _ = json.Marshal(a)
fmt.Printf("b: %s\n\n", string(b))
}
output:
a: {B:nil}
b: {}
UnmarshalJSON on B called
a: {B:3}
MarshalJSON on B called
b: {"b":7}
My questions are:
why UnmarshalJSON/MarshalJSON is not called with null/nil value on pointer type
how we can call UnmarshalJSON/MarshalJSON everytime when data is null/nil and type is a pointer instead of implementing UnmarshalJSON/MarshalJSON on A type and modify b property from level of A
For Short
Currently, unmarshaling/marshaling a Go struct will emit only the non-zero fields, since nil pointer is one zero value in Go, the UnmarshalJSON/MarshalJSON is not called in this case.
Also, it seems there are some related proposals
proposal: encoding/json: add omitzero option
proposal: encoding/json: allow returning nil from MarshalJSON to omit the field
However, there is no solution to resolve it now.
Per code Unmarshalers
Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op
// By convention, to approximate the behavior of Unmarshal itself,
// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
I am currently learning Go.
Following this link. On initCache function it accepts a evictionAlgo type parameter without * prefix so meaning it's not the pointer. See below the code I am referring.
type cache struct {
storage map[string]string
evictionAlgo evictionAlgo
capacity int
maxCapacity int
}
func initCache(e evictionAlgo) *cache {
storage := make(map[string]string)
return &cache{
storage: storage,
evictionAlgo: e,
capacity: 0,
maxCapacity: 2,
}
}
I got really confused. So my question is when or what scenario to consider when using a dereference pointer? And when should I not use dereference.
From my experience, you only have two situations where you would use a pointer as input or output. For output, I don't think it's ever really needed. My use case for it, it just as a convenience. Normally if you are returning an empty struct, you might do it like this:
package user
import "os"
type cache struct { dir string }
func newCache() (cache, error) {
c, e := os.UserCacheDir()
if e != nil {
return cache{}, e
}
return cache{c}, nil
}
but if you instead change to pointer, then you can use nil for the zero value:
func newCache() (*cache, error) {
c, e := os.UserCacheDir()
if e != nil { return nil, e }
return &cache{c}, nil
}
Regarding input, you should only need a pointer, if you need to change a field:
package user
type cache struct { dir string }
func (c *cache) set() {
c.dir = "north"
}
I thought you could change a slice or map field without a pointer receiver, but upon trying it only seems to work with pointer receiver as well.
I have the following:
https://play.golang.org/p/ADX6H-bh0CU
package main
import (
"encoding/xml"
"fmt"
)
type xmlMap map[string]string
type xmlMapEntry struct {
XMLName xml.Name
Value string `xml:",chardata"`
}
func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(m) == 0 {
return nil
}
err := e.EncodeToken(start)
if err != nil {
return err
}
for k, v := range m {
e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
}
return e.EncodeToken(start.End())
}
func main() {
type Family struct {
Siblings map[string]string
}
type MyFamily struct {
Siblings xmlMap `json:"siblings" xml:"siblings"`
}
// In reality, "f" comes from a function in an outside package...e.g. "f := somepackage.Function()"
f := &Family{
Siblings: map[string]string{"bob": "brother", "mary": "sister"},
}
var m = &MyFamily{}
*m = MyFamily(*f)
x, err := xml.Marshal(m)
if err != nil {
panic(err)
}
fmt.Println(string(x))
}
The Family struct comes from an outside package. I created MyFamily struct in order to add json and xml tags. If I try to xml encode MyFamily then I get an error:
xml: unsupported type: map[string]string
This is easily resolved by implementing the custom XML marshaller that I have done.
This creates a separate problem. I have tried to simplify the example but "f" comes from a function in an outside package...e.g. "f := somepackage.Function()". When I try to Alias f to m I get the error:
cannot convert *f (type Family) to type MyFamily
This happens because Family has a map[string]string but MyFamily has xmlMap. Even though they are the same underlying type I get that error.
The question is, how do I implement my custom struct tags AND be able to XML encode a map?
EDIT:
This is a simple example and can just be resolved by
var m = &MyFamily{Siblings: f.Siblings}
Since there are MANY fields in both Family and MyFamily this is just inefficient and just wanted to know if there's a better way.
In Go, http form data (e.g. from a POST or PUT request) can be accessed as a map of the form map[string][]string. I'm having a hard time converting this to structs in a generalizable way.
For example, I want to load a map like:
m := map[string][]string {
"Age": []string{"20"},
"Name": []string{"John Smith"},
}
Into a model like:
type Person struct {
Age int
Name string
}
So I'm trying to write a function with the signature LoadModel(obj interface{}, m map[string][]string) []error that will load the form data into an interface{} that I can type cast back to a Person. Using reflection so that I can use it on any struct type with any fields, not just a Person, and so that I can convert the string from the http data to an int, boolean, etc as necessary.
Using the answer to this question in golang, using reflect, how do you set the value of a struct field? I can set the value of a person using reflect, e.g.:
p := Person{25, "John"}
reflect.ValueOf(&p).Elem().Field(1).SetString("Dave")
But then I'd have to copy the load function for every type of struct I have. When I try it for an interface{} it doesn't work.
pi := (interface{})(p)
reflect.ValueOf(&pi).Elem().Field(1).SetString("Dave")
// panic: reflect: call of reflect.Value.Field on interface Value
How can I do this in the general case? Or even better, is there a more idiomatic Go way to accomplish what I'm trying to do?
You need to make switches for the general case, and load the different field types accordingly. This is basic part.
It gets harder when you have slices in the struct (then you have to load them up to the number of elements in the form field), or you have nested structs.
I have written a package that does this. Please see:
http://www.gorillatoolkit.org/pkg/schema
For fun, I tried it out. Note that I cheated a little bit (see comments), but you should get the picture. There is usually a cost to use reflection vs statically typed assignments (like nemo's answer), so be sure to weigh that in your decision (I haven't benchmarked it though).
Also, obvious disclaimer, I haven't tested all edge cases, etc, etc. Don't just copy paste this in production code :)
So here goes:
package main
import (
"fmt"
"reflect"
"strconv"
)
type Person struct {
Age int
Name string
Salary float64
}
// I cheated a little bit, made the map's value a string instead of a slice.
// Could've used just the index 0 instead, or fill an array of structs (obj).
// Either way, this shows the reflection steps.
//
// Note: no error returned from this example, I just log to stdout. Could definitely
// return an array of errors, and should catch a panic since this is possible
// with the reflect package.
func LoadModel(obj interface{}, m map[string]string) {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panic! %v\n", e)
}
}()
val := reflect.ValueOf(obj)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
// Loop over map, try to match the key to a field
for k, v := range m {
if f := val.FieldByName(k); f.IsValid() {
// Is it assignable?
if f.CanSet() {
// Assign the map's value to this field, converting to the right data type.
switch f.Type().Kind() {
// Only a few kinds, just to show the basic idea...
case reflect.Int:
if i, e := strconv.ParseInt(v, 0, 0); e == nil {
f.SetInt(i)
} else {
fmt.Printf("Could not set int value of %s: %s\n", k, e)
}
case reflect.Float64:
if fl, e := strconv.ParseFloat(v, 0); e == nil {
f.SetFloat(fl)
} else {
fmt.Printf("Could not set float64 value of %s: %s\n", k, e)
}
case reflect.String:
f.SetString(v)
default:
fmt.Printf("Unsupported format %v for field %s\n", f.Type().Kind(), k)
}
} else {
fmt.Printf("Key '%s' cannot be set\n", k)
}
} else {
// Key does not map to a field in obj
fmt.Printf("Key '%s' does not have a corresponding field in obj %+v\n", k, obj)
}
}
}
func main() {
m := map[string]string{
"Age": "36",
"Name": "Johnny",
"Salary": "1400.33",
"Ignored": "True",
}
p := new(Person)
LoadModel(p, m)
fmt.Printf("After LoadModel: Person=%+v\n", p)
}
I'd propose to use a specific interface instead of interface{} in your LoadModel
which your type has to implement in order to be loaded.
For example:
type Loadable interface{
LoadValue(name string, value []string)
}
func LoadModel(loadable Loadable, data map[string][]string) {
for key, value := range data {
loadable.LoadValue(key, value)
}
}
And your Person implements Loadable by implementing LoadModel like this:
type Person struct {
Age int
Name string
}
func (p *Person) LoadValue(name string, value []string) {
switch name {
case "Age":
p.Age, err = strconv.Atoi(value[0])
// etc.
}
}
This is the way, the encoding/binary package or the encoding/json package work, for example.