Merge pull request #267 from skinass/marshal_unmarshal_unknown_fields

add interfaces to provide marshal/unmarshal logic for unknown fields in struct
This commit is contained in:
Anton Sulaev
2020-02-18 11:42:23 +03:00
committed by GitHub
7 changed files with 137 additions and 0 deletions
+2
View File
@@ -19,6 +19,7 @@ generate: build
./tests/embedded_type.go \
./tests/reference_to_pointer.go \
./tests/html.go \
./tests/unknown_fields.go \
bin/easyjson -all ./tests/data.go
bin/easyjson -all ./tests/nothing.go
@@ -34,6 +35,7 @@ generate: build
bin/easyjson ./tests/reference_to_pointer.go
bin/easyjson ./tests/key_marshaler_map.go
bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go
bin/easyjson ./tests/unknown_fields.go
test: generate
go test \
+12
View File
@@ -94,6 +94,16 @@ func hasCustomUnmarshaler(t reflect.Type) bool {
t.Implements(reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem())
}
func hasUnknownsUnmarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsUnmarshaler)(nil)).Elem())
}
func hasUnknownsMarshaler(t reflect.Type) bool {
t = reflect.PtrTo(t)
return t.Implements(reflect.TypeOf((*easyjson.UnknownsMarshaler)(nil)).Elem())
}
// genTypeDecoderNoCheck generates decoding code for the type t.
func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags fieldTags, indent int) error {
ws := strings.Repeat(" ", indent)
@@ -485,6 +495,8 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
Reason: "unknown field",
Data: key,
})`)
} else if hasUnknownsUnmarshaler(t) {
fmt.Fprintln(g.out, " out.UnmarshalUnknown(in, key)")
} else {
fmt.Fprintln(g.out, " in.SkipRecursive()")
}
+8
View File
@@ -393,6 +393,14 @@ func (g *Generator) genStructEncoder(t reflect.Type) error {
}
}
if hasUnknownsMarshaler(t) {
if !firstCondition {
fmt.Fprintln(g.out, " in.MarshalUnknowns(out, false)")
} else {
fmt.Fprintln(g.out, " in.MarshalUnknowns(out, first)")
}
}
fmt.Fprintln(g.out, " out.RawByte('}')")
fmt.Fprintln(g.out, "}")
+10
View File
@@ -26,6 +26,16 @@ type Optional interface {
IsDefined() bool
}
// UnknownsUnmarshaler provides a method to unmarshal unknown struct fileds and save them as you want
type UnknownsUnmarshaler interface {
UnmarshalUnknown(in *jlexer.Lexer, key string)
}
// UnknownsMarshaler provides a method to write additional struct fields
type UnknownsMarshaler interface {
MarshalUnknowns(w *jwriter.Writer, first bool)
}
// Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied
// from a chain of smaller chunks.
func Marshal(v Marshaler) ([]byte, error) {
+17
View File
@@ -0,0 +1,17 @@
package tests
import "github.com/mailru/easyjson"
//easyjson:json
type StructWithUnknownsProxy struct {
easyjson.UnknownFieldsProxy
Field1 string
}
//easyjson:json
type StructWithUnknownsProxyWithOmitempty struct {
easyjson.UnknownFieldsProxy
Field1 string `json:",omitempty"`
}
+54
View File
@@ -0,0 +1,54 @@
package tests
import (
"reflect"
"testing"
)
func TestUnknownFieldsProxy(t *testing.T) {
baseJson := `{"Field1":"123","Field2":"321"}`
s := StructWithUnknownsProxy{}
err := s.UnmarshalJSON([]byte(baseJson))
if err != nil {
t.Errorf("UnmarshalJSON didn't expect error: %v", err)
}
if s.Field1 != "123" {
t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1)
}
data, err := s.MarshalJSON()
if err != nil {
t.Errorf("MarshalJSON didn't expect error: %v", err)
}
if !reflect.DeepEqual(baseJson, string(data)) {
t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data))
}
}
func TestUnknownFieldsProxyWithOmitempty(t *testing.T) {
baseJson := `{"Field1":"123","Field2":"321"}`
s := StructWithUnknownsProxyWithOmitempty{}
err := s.UnmarshalJSON([]byte(baseJson))
if err != nil {
t.Errorf("UnmarshalJSON didn't expect error: %v", err)
}
if s.Field1 != "123" {
t.Errorf("UnmarshalJSON expected to parse Field1 as \"123\". got: %v", s.Field1)
}
data, err := s.MarshalJSON()
if err != nil {
t.Errorf("MarshalJSON didn't expect error: %v", err)
}
if !reflect.DeepEqual(baseJson, string(data)) {
t.Errorf("MarshalJSON expected to gen: %v. got: %v", baseJson, string(data))
}
}
+34
View File
@@ -0,0 +1,34 @@
package easyjson
import (
json "encoding/json"
jlexer "github.com/mailru/easyjson/jlexer"
"github.com/mailru/easyjson/jwriter"
)
// UnknownFieldsProxy implemets UnknownsUnmarshaler and UnknownsMarshaler
// use it as embedded field in your structure to parse and then serialize unknown struct fields
type UnknownFieldsProxy struct {
unknownFields map[string]interface{}
}
func (s *UnknownFieldsProxy) UnmarshalUnknown(in *jlexer.Lexer, key string) {
if s.unknownFields == nil {
s.unknownFields = make(map[string]interface{}, 1)
}
s.unknownFields[key] = in.Interface()
}
func (s UnknownFieldsProxy) MarshalUnknowns(out *jwriter.Writer, first bool) {
for key, val := range s.unknownFields {
if first {
first = false
} else {
out.RawByte(',')
}
out.String(string(key))
out.RawByte(':')
out.Raw(json.Marshal(val))
}
}