go-json icon indicating copy to clipboard operation
go-json copied to clipboard

Decoding into interface bug

Open GRbit opened this issue 3 years ago • 6 comments

When I tried to unmarshal bytes into a struct hidden under a specific interface, "go-json" failed to unmarshal it. At the same time, go standard lib or json-iterator feels just fine with this approach. Since you claim that your library is 100% compatible with the standard library, it makes sense to fix this bug or to change your README and add this exact case.

Code to reproduce the bug:

package main

import (
	"fmt"
	"github.com/goccy/go-json"
)

type EsDocument interface {
	IndexName() string
}

type Property struct {
	Name string `json:"name"`
}

func (p Property) IndexName() string {
	return "properties"
}

func main() {
	b := []byte(`{"name":"Test Property to remove"}`)
	p := Property{}

	unmarshallInterface(b, &p)

	fmt.Println(p)
}

func unmarshallInterface(body []byte, doc EsDocument) {
	if err := json.Unmarshal(body, &doc); err != nil {
		panic(err)
	}
}

Output

$ go run main.go
panic: json: cannot unmarshal main.EsDocument into Go value of type main.EsDocument

goroutine 1 [running]:
main.unmarshallInterface(...)
	/home/grbit/ssd/go/src/tst/main.go:31
main.main()
	/home/grbit/ssd/go/src/tst/main.go:24 +0x16d
exit status 2

If you change doc type name from EsDocument to just interface{} it'll work just fine.

package main

import (
	"fmt"
	"github.com/goccy/go-json"
)

type Property struct {
	Name string `json:"name"`
}

func main() {
	b := []byte(`{"name":"Test Property to remove"}`)
	p := Property{}

	unmarshallInterface(b, &p)

	fmt.Println(p)
}

func unmarshallInterface(body []byte, doc interface{}) {
	if err := json.Unmarshal(body, &doc); err != nil {
		panic(err)
	}
}

Output

$ go run main.go
{Test Property to remove}

GRbit avatar Oct 03 '22 07:10 GRbit

Solved!

cmetallo42 avatar Oct 06 '22 10:10 cmetallo42

Hello @goccy, in #406 you said that you would fix this issue, the fix seems to be pretty small by itself, is there any chance you will do that any time soon? Thanks!

mymmrac avatar Jan 06 '24 12:01 mymmrac

@mymmrac are you sure that the bug persists with the latest version? For me it works now. Maybe your case is a little bit different? If so, it would be great if you could provide a test case reproducing the problem.

GRbit avatar Jan 08 '24 08:01 GRbit

@GRbit yes, it reproduces, here is an example:

package main

import (
	// "encoding/json"
	"fmt"

	"github.com/goccy/go-json"
)

func main() {
	s := Something{}
	s.Thing = &ThingA{}
	err := json.Unmarshal([]byte(`{"thing": {"a": 1}}`), &s)
	if err != nil {
		panic(err)
	}
	fmt.Println(s.Thing)
}

type Something struct {
	Thing Thing `json:"thing"`
}

type Thing interface {
	F()
}

type ThingA struct {
	A int `json:"a"`
}

func (t *ThingA) F() {}

With github.com/goccy/go-json - current master df897ae

panic: json: cannot unmarshal main.Thing into Go struct field Something.Thing of type main.Thing

With encoding/json - no issues, parsed as expected (s.Thing.A == 1)

mymmrac avatar Jan 08 '24 11:01 mymmrac

With this changes it works (from PR that was closed, #406):

replace github.com/goccy/go-json => github.com/cmetallo42/go-json v0.0.0-20221114111108-7fed434e8044

mymmrac avatar Jan 08 '24 11:01 mymmrac

@mymmrac Indeed, you are right.

For some reason I thought that at some point it was fixed in goccy repository. Thank you for bringing this up again.

GRbit avatar Jan 08 '24 16:01 GRbit