Repository: goccy/go-json
Pull request #321 (2022-01-13 09:05:01)

Add invalid decoder to delay type error judgment at decode
fix https://github.com/goccy/go-json/issues/320


Patch:
diff --git a/internal/decoder/compile.go b/internal/decoder/compile.go
index 08dd044..9cafc06 100644
--- a/internal/decoder/compile.go
+++ b/internal/decoder/compile.go
@@ -9,7 +9,6 @@ import (
 	"unicode"
 	"unsafe"
 
-	"github.com/goccy/go-json/internal/errors"
 	"github.com/goccy/go-json/internal/runtime"
 )
 
@@ -126,13 +125,7 @@ func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecode
 	case reflect.Func:
 		return compileFunc(typ, structName, fieldName)
 	}
-	return nil, &errors.UnmarshalTypeError{
-		Value:  "object",
-		Type:   runtime.RType2Type(typ),
-		Offset: 0,
-		Struct: structName,
-		Field:  fieldName,
-	}
+	return newInvalidDecoder(typ, structName, fieldName), nil
 }
 
 func isStringTagSupportedType(typ *runtime.Type) bool {
@@ -174,17 +167,9 @@ func compileMapKey(typ *runtime.Type, structName, fieldName string, structTypeTo
 		case *ptrDecoder:
 			dec = t.dec
 		default:
-			goto ERROR
+			return newInvalidDecoder(typ, structName, fieldName), nil
 		}
 	}
-ERROR:
-	return nil, &errors.UnmarshalTypeError{
-		Value:  "object",
-		Type:   runtime.RType2Type(typ),
-		Offset: 0,
-		Struct: structName,
-		Field:  fieldName,
-	}
 }
 
 func compilePtr(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
diff --git a/internal/decoder/invalid.go b/internal/decoder/invalid.go
new file mode 100644
index 0000000..1ef50a7
--- /dev/null
+++ b/internal/decoder/invalid.go
@@ -0,0 +1,45 @@
+package decoder
+
+import (
+	"reflect"
+	"unsafe"
+
+	"github.com/goccy/go-json/internal/errors"
+	"github.com/goccy/go-json/internal/runtime"
+)
+
+type invalidDecoder struct {
+	typ        *runtime.Type
+	kind       reflect.Kind
+	structName string
+	fieldName  string
+}
+
+func newInvalidDecoder(typ *runtime.Type, structName, fieldName string) *invalidDecoder {
+	return &invalidDecoder{
+		typ:        typ,
+		kind:       typ.Kind(),
+		structName: structName,
+		fieldName:  fieldName,
+	}
+}
+
+func (d *invalidDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
+	return &errors.UnmarshalTypeError{
+		Value:  "object",
+		Type:   runtime.RType2Type(d.typ),
+		Offset: s.totalOffset(),
+		Struct: d.structName,
+		Field:  d.fieldName,
+	}
+}
+
+func (d *invalidDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
+	return 0, &errors.UnmarshalTypeError{
+		Value:  "object",
+		Type:   runtime.RType2Type(d.typ),
+		Offset: cursor,
+		Struct: d.structName,
+		Field:  d.fieldName,
+	}
+}


Test patch:
diff --git a/decode_test.go b/decode_test.go
index ea88d76..9be2ba0 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -3776,3 +3776,16 @@ func TestIssue282(t *testing.T) {
 		t.Fatalf("failed to assign map value")
 	}
 }
+
+func TestDecodeStructFieldMap(t *testing.T) {
+	type Foo struct {
+		Bar map[float64]float64 `json:"bar,omitempty"`
+	}
+	var v Foo
+	if err := json.Unmarshal([]byte(`{"name":"test"}`), &v); err != nil {
+		t.Fatal(err)
+	}
+	if v.Bar != nil {
+		t.Fatalf("failed to decode v.Bar = %+v", v.Bar)
+	}
+}