commit 30ff297aeb4aa49b2c60f0c0b9a49a7140492166
parent 1f3d8b54c1fd186494c8f1fe59f24e1f26a9cf18
Author: adrianwit <adrianwit@gmail.com>
Date: Thu, 13 Sep 2018 12:19:39 -0700
added advaned code generation
Diffstat:
27 files changed, 3154 insertions(+), 0 deletions(-)
diff --git a/codegen/README.md b/codegen/README.md
@@ -0,0 +1,65 @@
+# Gojay code generator
+
+This package provides a command line tool to generate gojay's marshaling and unmarshing interface implementation for custom type(s)
+
+
+## Get started
+
+```sh
+go install github.com/francoispqt/gojay/gojaygen
+```
+
+## Generate code
+
+### Basic command
+
+The basic command is straightforward and easy to use:
+```sh
+cd $GOPATH/src/mycoany/myproject
+gojaygen -p myproject
+```
+If you just want to the output to stdout, omit the third parameter.
+
+### Using flags
+
+- s file/dir to path, can be a relative or absolute path
+- t root types to generate with its all dependencies (comma separated)
+- a annotation tag used to read meta data (default: json)
+- o output file (relative or absolute path)
+- p reuse object witt sync.Pool
+
+Examples:
+
+- Specific type in a go package, write to a file:
+```sh
+gojay -s /tmp/myproj -t SomeType -o output.go
+```
+
+- Specific types in a go package, with custom tag, write to a file::
+```sh
+gojay -s somegofile.go -a gojay -t SomeType
+```
+
+
+## Generator tags
+
+You can add tags to your structs to control:
+
+- the JSON key
+- skip a struct field
+- the use of omit empty methods for marshaling
+- timeFormat (java style data format)
+- timeLayout (golang time layout)
+
+
+### Example:
+```go
+type A struct {
+ Str string `json:"string"`
+ StrOmitEmpty string `json:"stringOrEmpty,omitempty"`
+ Skip string `json:"-"`
+ StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"`
+ EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"`
+}
+```
+
diff --git a/codegen/field.go b/codegen/field.go
@@ -0,0 +1,161 @@
+package codegen
+
+import (
+ "fmt"
+ "github.com/viant/toolbox"
+ "strings"
+)
+
+//Field represents a field.
+type Field struct {
+ Key string
+ Init string
+ OmitEmpty string
+ TimeLayout string
+ Name string
+ Accessor string
+ Mutator string
+ Receiver string //alias and type name
+ Alias string //object alias name
+ Var string //variable for this field
+ Type string
+ RawType string
+ HelperType string
+ ComponentType string
+ RawComponentType string
+ IsPointerComponent bool
+
+ PointerModifier string //takes field pointer, "&" if field is not a pointer type
+ DereferenceModifier string //take pointer value, i.e "*" if field has a pointer type
+
+ ComponentPointerModifier string //takes item pointer if needed,i.e
+ ComponentDereferenceModifier string //de reference value if needed, i.e
+ ComponentInitModifier string //takes item pointer if type is not a pointer type
+ ComponentInit string //initialises component type
+
+ DecodingMethod string
+ EncodingMethod string
+ PoolName string //pool name associated with this field
+ ResetDependency string
+ Reset string
+ IsAnonymous bool
+ IsPointer bool
+ IsSlice bool
+}
+
+//NewField returns a new field
+func NewField(owner *Struct, field *toolbox.FieldInfo, fieldType *toolbox.TypeInfo) (*Field, error) {
+ typeName := normalizeTypeName(field.TypeName)
+ var result = &Field{
+ IsAnonymous: field.IsAnonymous,
+ Name: field.Name,
+ RawType: field.TypeName,
+ IsPointer: field.IsPointer,
+ Key: getJSONKey(owner.options, field),
+ Receiver: owner.Alias + " *" + owner.TypeInfo.Name,
+ Type: typeName,
+ Mutator: owner.Alias + "." + field.Name,
+ Accessor: owner.Alias + "." + field.Name,
+ ComponentType: field.ComponentType,
+ IsPointerComponent: field.IsPointerComponent,
+ Var: firstLetterToLowercase(field.Name),
+ Init: fmt.Sprintf("%v{}", typeName),
+ TimeLayout: "time.RFC3339",
+ IsSlice: field.IsSlice,
+ PoolName: getPoolName(field.TypeName),
+ Alias: owner.Alias,
+ Reset: "nil",
+ }
+ var err error
+ if field.IsPointer {
+ result.DereferenceModifier = "*"
+ result.Init = "&" + result.Init
+ } else {
+ result.PointerModifier = "&"
+
+ }
+ if field.IsSlice {
+ result.HelperType = getSliceHelperTypeName(field.ComponentType, field.IsPointerComponent)
+ result.PoolName = getPoolName(field.ComponentType)
+ } else if fieldType != nil {
+ result.HelperType = getSliceHelperTypeName(fieldType.Name, field.IsPointerComponent)
+ }
+
+ if options := getTagOptions(field.Tag, "timeLayout"); len(options) > 0 {
+ result.TimeLayout = wrapperIfNeeded(options[0], `"`)
+ } else if options := getTagOptions(field.Tag, "timeFormat"); len(options) > 0 {
+ result.TimeLayout = wrapperIfNeeded(toolbox.DateFormatToLayout(options[0]), `"`)
+ }
+ if strings.Contains(field.Tag, "omitEmpty") {
+ result.OmitEmpty = "OmitEmpty"
+ }
+ if owner.options.PoolObjects {
+ if field.IsPointer && !strings.HasSuffix(field.TypeName, ".Time") {
+ poolName := getPoolName(field.TypeName)
+ result.Init = fmt.Sprintf(`%v.Get().(*%v)`, poolName, field.TypeName)
+ }
+ }
+
+ encodingMethod := field.ComponentType
+ if encodingMethod == "" {
+ encodingMethod = result.Type
+ }
+ result.DecodingMethod = firstLetterToUppercase(encodingMethod)
+ result.EncodingMethod = firstLetterToUppercase(encodingMethod)
+
+ switch typeName {
+ case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
+ result.Reset = "0"
+ case "float32", "float64":
+ result.Reset = "0.0"
+ case "string":
+ result.Reset = `""`
+
+ case "bool":
+ result.Reset = "false"
+ default:
+ if field.IsSlice && owner.Type(field.ComponentType) != nil {
+ var itemPointer = ""
+ if !field.IsPointerComponent {
+ itemPointer = "&"
+ }
+ result.ResetDependency, err = expandFieldTemplate(poolSliceInstanceRelease, struct {
+ PoolName string
+ Accessor string
+ PointerModifier string
+ }{PoolName: result.PoolName, Accessor: result.Accessor, PointerModifier: itemPointer})
+ if err != nil {
+ return nil, err
+ }
+
+ } else if field.IsPointer && fieldType != nil {
+ result.ResetDependency, err = expandFieldTemplate(poolInstanceRelease, struct {
+ PoolName string
+ Accessor string
+ }{PoolName: result.PoolName, Accessor: result.Accessor})
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ }
+ if field.IsSlice || field.IsPointer {
+ result.Reset = "nil"
+ }
+
+ if result.IsPointerComponent {
+ result.ComponentInit = "&" + result.ComponentType + "{}"
+ result.RawComponentType = "*" + result.ComponentType
+
+ result.ComponentDereferenceModifier = "*"
+ result.ComponentInitModifier = "&"
+
+ } else {
+ result.ComponentInit = result.ComponentType + "{}"
+ result.RawComponentType = result.ComponentType
+
+ result.ComponentPointerModifier = "&"
+ }
+
+ return result, nil
+}
diff --git a/codegen/generator.go b/codegen/generator.go
@@ -0,0 +1,167 @@
+package codegen
+
+import (
+ "fmt"
+ "github.com/viant/toolbox"
+ "io/ioutil"
+ "strings"
+)
+
+type Generator struct {
+ fileInfo *toolbox.FileSetInfo
+ types map[string]string
+ structTypes map[string]string
+ sliceTypes map[string]string
+ pooledObjects map[string]string
+ poolInit map[string]string
+ imports map[string]bool
+ filedInit []string
+ Pkg string
+ Code string
+ Init string
+ Imports string
+ options *Options
+}
+
+func (g *Generator) Type(typeName string) *toolbox.TypeInfo {
+ return g.fileInfo.Type(typeName)
+}
+
+func (g *Generator) addImport(pkg string) {
+ g.imports[`"`+pkg+`"`] = true
+}
+
+func (g *Generator) init() {
+ g.filedInit = []string{}
+ g.imports = map[string]bool{}
+ g.pooledObjects = map[string]string{}
+ g.structTypes = map[string]string{}
+ g.sliceTypes = map[string]string{}
+ g.poolInit = map[string]string{}
+ g.addImport("github.com/francoispqt/gojay")
+}
+
+func (g *Generator) Generate(options *Options) error {
+ if err := options.Validate(); err != nil {
+ return err
+ }
+ g.options = options
+ g.init()
+
+ if options.PoolObjects {
+ g.addImport("sync")
+ }
+ if err := g.readPackageCode(options.Source); err != nil {
+ return err
+ }
+ for _, rootType := range options.Types {
+ if err := g.generateStructCode(rootType); err != nil {
+ return err
+ }
+ }
+ g.Imports = strings.Join(toolbox.MapKeysToStringSlice(g.imports), "\n")
+ return g.writeCode()
+}
+
+func (g *Generator) writeCode() error {
+ var generatedCode = []string{}
+ for _, code := range g.pooledObjects {
+ generatedCode = append(generatedCode, code)
+ }
+ generatedCode = append(generatedCode, "")
+ for _, code := range g.sliceTypes {
+ generatedCode = append(generatedCode, code)
+ }
+ generatedCode = append(generatedCode, "")
+ for _, code := range g.structTypes {
+ generatedCode = append(generatedCode, code)
+ }
+
+ for _, code := range g.poolInit {
+ if g.Init != "" {
+ g.Init += "\n"
+ }
+ g.Init += code
+ }
+ g.Code = strings.Join(generatedCode, "\n")
+
+ expandedCode, err := expandBlockTemplate(fileCode, g)
+ if err != nil {
+ return err
+ }
+ if g.options.Dest == "" {
+ fmt.Print(expandedCode)
+ return nil
+ }
+ return ioutil.WriteFile(g.options.Dest, []byte(expandedCode), 0644)
+}
+
+func (g *Generator) generatePrimitiveArray(field *Field) error {
+ key := field.ComponentType + toolbox.AsString(field.IsPointerComponent)
+ if _, ok := g.sliceTypes[key]; ok {
+ return nil
+ }
+ code, err := expandBlockTemplate(baseTypeSlice, field)
+ g.sliceTypes[key] = code
+ return err
+}
+
+func (g *Generator) generateObjectArray(field *Field) error {
+ if _, ok := g.sliceTypes[field.RawComponentType]; ok {
+ return nil
+ }
+
+ if err := g.generateStructCode(field.ComponentType); err != nil {
+ return err
+ }
+ code, err := expandBlockTemplate(structTypeSlice, field)
+ if err != nil {
+ return err
+ }
+ g.sliceTypes[field.RawComponentType] = code
+ return err
+}
+
+func (g *Generator) generatePool(structType string) error {
+ if !g.options.PoolObjects {
+ return nil
+ }
+ var err error
+ if g.pooledObjects[structType], err = expandBlockTemplate(poolVar, struct {
+ PoolName string
+ }{getPoolName(structType)}); err == nil {
+ g.poolInit[structType], err = expandBlockTemplate(poolInit, struct {
+ PoolName string
+ Type string
+ }{getPoolName(structType), structType})
+ }
+ return err
+
+}
+
+func (g *Generator) generateStructCode(structType string) error {
+ structType = normalizeTypeName(structType)
+ typeInfo := g.Type(structType)
+ if typeInfo == nil {
+ return nil
+ }
+ if _, hasCode := g.structTypes[structType]; hasCode {
+ return nil
+ }
+ g.generatePool(structType)
+ aStruct := NewStruct(typeInfo, g)
+ code, err := aStruct.Generate()
+ if err != nil {
+ return err
+ }
+ g.structTypes[structType] = code
+ return nil
+}
+
+func (g *Generator) readPackageCode(pkgPath string) error {
+ var err error
+ fragments := strings.Split(pkgPath, "/")
+ g.Pkg = fragments[len(fragments)-1]
+ g.fileInfo, err = toolbox.NewFileSetInfo(pkgPath)
+ return err
+}
diff --git a/codegen/generator_test.go b/codegen/generator_test.go
@@ -0,0 +1,72 @@
+package codegen
+
+import (
+ "github.com/stretchr/testify/assert"
+ "github.com/viant/toolbox"
+ "log"
+ "path"
+ "testing"
+)
+
+func TestGenerator_Generate(t *testing.T) {
+
+ parent := path.Join(toolbox.CallerDirectory(3), "test")
+
+ var useCases = []struct {
+ description string
+ options *Options
+ hasError bool
+ }{
+ {
+ description: "basic struct code generation",
+ options: &Options{
+ Source: path.Join(parent, "basic_struct"),
+ Types: []string{"Message"},
+ Dest: path.Join(parent, "basic_struct", "encoding.go"),
+ },
+ },
+
+ {
+ description: "struct with pool code generation",
+ options: &Options{
+ Source: path.Join(parent, "pooled_struct"),
+ Types: []string{"Message"},
+ Dest: path.Join(parent, "pooled_struct", "encoding.go"),
+ PoolObjects: true,
+ },
+ },
+ {
+ description: "struct with embedded type code generation",
+ options: &Options{
+ Source: path.Join(parent, "embedded_struct"),
+ Types: []string{"Message"},
+ Dest: path.Join(parent, "embedded_struct", "encoding.go"),
+ PoolObjects: false,
+ },
+ },
+ {
+ description: "struct with json annotation and time/foarmat|layouat generation",
+ options: &Options{
+ Source: path.Join(parent, "annotated_struct"),
+ Types: []string{"Message"},
+ Dest: path.Join(parent, "annotated_struct", "encoding.go"),
+ PoolObjects: false,
+ TagName: "json",
+ },
+ },
+ }
+
+ for _, useCase := range useCases {
+ gen := &Generator{}
+ err := gen.Generate(useCase.options)
+ if useCase.hasError {
+ assert.NotNil(t, err, useCase.description)
+ continue
+ }
+ if !assert.Nil(t, err, useCase.description) {
+ log.Fatal(err)
+ continue
+ }
+ }
+
+}
diff --git a/codegen/helper.go b/codegen/helper.go
@@ -0,0 +1,88 @@
+package codegen
+
+import (
+ "github.com/viant/toolbox"
+ "reflect"
+ "strings"
+)
+
+func firstLetterToUppercase(text string) string {
+ return strings.ToUpper(string(text[0:1])) + string(text[1:])
+}
+
+func firstLetterToLowercase(text string) string {
+ return strings.ToLower(string(text[0:1])) + string(text[1:])
+}
+
+func extractReceiverAlias(structType string) string {
+ var result = string(structType[0])
+ for i := len(structType) - 1; i > 0; i-- {
+ aChar := string(structType[i])
+ lowerChar := strings.ToLower(aChar)
+ if lowerChar != aChar {
+ result = lowerChar
+ break
+ }
+ }
+ return strings.ToLower(result)
+}
+
+func getTagOptions(tag, key string) []string {
+ if tag == "" {
+ return nil
+ }
+ var structTag = reflect.StructTag(strings.Replace(tag, "`", "", len(tag)))
+ options, ok := structTag.Lookup(key)
+ if !ok {
+ return nil
+ }
+ return strings.Split(options, ",")
+}
+
+func getSliceHelperTypeName(typeName string, isPointer bool) string {
+ if typeName == "" {
+ return ""
+ }
+ var pluralName = firstLetterToUppercase(typeName) + "s"
+ if isPointer {
+ pluralName += "Ptr"
+ }
+ return pluralName
+}
+
+func isSkipable(options *Options, field *toolbox.FieldInfo) bool {
+ if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 {
+ for _, candidate := range options {
+ if candidate == "-" {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func wrapperIfNeeded(text, wrappingChar string) string {
+ if strings.HasPrefix(text, wrappingChar) {
+ return text
+ }
+ return wrappingChar + text + wrappingChar
+}
+
+func getPoolName(typeName string) string {
+ typeName = strings.Replace(typeName, "*", "", 1)
+ return typeName + "Pool"
+}
+
+func getJSONKey(options *Options, field *toolbox.FieldInfo) string {
+ var key = field.Name
+ if field.Tag != "" {
+ if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 {
+ key = options[0]
+ }
+ }
+ return key
+}
+
+func normalizeTypeName(typeName string) string {
+ return strings.Replace(typeName, "*", "", strings.Count(typeName, "*"))
+}
diff --git a/codegen/options.go b/codegen/options.go
@@ -0,0 +1,54 @@
+package codegen
+
+import (
+ "flag"
+ "github.com/go-errors/errors"
+ "github.com/viant/toolbox"
+ "github.com/viant/toolbox/url"
+ "strings"
+)
+
+type Options struct {
+ Source string
+ Dest string
+ Types []string
+ PoolObjects bool
+ TagName string
+}
+
+func (o *Options) Validate() error {
+ if o.Source == "" {
+ return errors.New("Source was empty")
+ }
+ if o.Dest == "" {
+ return errors.New("Dest was empty")
+ }
+ if len(o.Types) == 0 {
+ return errors.New("Types was empty")
+ }
+ return nil
+}
+
+const (
+ optionKeySource = "s"
+ optionKeyDest = "o"
+ optionKeyTypes = "t"
+ optionKeyTagName = "a"
+ optionKeyPoolObjects = "p"
+)
+
+//NewOptionsWithFlagSet creates a new options for the supplide flagset
+func NewOptionsWithFlagSet(set *flag.FlagSet) *Options {
+ toolbox.Dump(set)
+
+ var result = &Options{}
+ result.Dest = set.Lookup(optionKeyDest).Value.String()
+ result.Source = set.Lookup(optionKeySource).Value.String()
+ result.PoolObjects = toolbox.AsBoolean(set.Lookup(optionKeyPoolObjects).Value.String())
+ result.TagName = set.Lookup(optionKeyTagName).Value.String()
+ result.Types = strings.Split(set.Lookup(optionKeyTypes).Value.String(), ",")
+ if result.Source == "" {
+ result.Source = url.NewResource(".").ParsedURL.Path
+ }
+ return result
+}
diff --git a/codegen/struct.go b/codegen/struct.go
@@ -0,0 +1,296 @@
+package codegen
+
+import (
+ "fmt"
+ "github.com/viant/toolbox"
+ "strings"
+)
+
+type Struct struct {
+ *toolbox.TypeInfo
+ referenced *toolbox.TypeInfo
+ *Generator
+ Alias string
+ Init string
+ Body string
+}
+
+//Generate generates decoderCode + structRelease + encoderCode
+func (s *Struct) Generate() (string, error) {
+ return s.generateEncoding(s.TypeInfo)
+}
+
+func (s *Struct) generateEncoding(structInfo *toolbox.TypeInfo) (string, error) {
+ var initEmbedded, decodingCases, err = s.generateFieldDecoding(structInfo.Fields())
+ if err != nil {
+ return "", err
+ }
+
+ encodingCases, err := s.generateFieldEncoding(structInfo.Fields())
+ if err != nil {
+ return "", err
+ }
+ var resetCode = ""
+ if s.options.PoolObjects {
+ resetCode, err = s.generateReset(structInfo.Fields())
+ if err != nil {
+ return "", err
+ }
+ }
+ var data = struct {
+ Receiver string
+ Alias string
+ InitEmbedded string
+ EncodingCases string
+ DecodingCases string
+ Reset string
+ FieldCount int
+ }{
+ Receiver: s.Alias + " *" + s.Name,
+ DecodingCases: strings.Join(decodingCases, "\n"),
+ EncodingCases: strings.Join(encodingCases, "\n"),
+ FieldCount: len(decodingCases),
+ InitEmbedded: initEmbedded,
+ Reset: resetCode,
+ Alias: s.Alias,
+ }
+ return expandBlockTemplate(encodingStructType, data)
+}
+
+func (s *Struct) generateReset(fields []*toolbox.FieldInfo) (string, error) {
+ fieldReset, err := s.generateFieldReset(fields)
+ if err != nil {
+ return "", nil
+ }
+ return expandBlockTemplate(resetStruct, struct {
+ Reset string
+ Receiver string
+ }{
+ Reset: strings.Join(fieldReset, "\n"),
+ Receiver: s.Alias + " *" + s.Name,
+ })
+}
+
+func (s *Struct) generateFieldReset(fields []*toolbox.FieldInfo) ([]string, error) {
+ fieldReset := []string{}
+ for i := range fields {
+ var templateKey = -1
+ fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName))
+ field, err := NewField(s, fields[i], fieldTypeInfo)
+ if err != nil {
+ return nil, err
+ }
+ if field.IsPointer || field.IsSlice || (fieldTypeInfo != nil && fieldTypeInfo.IsSlice) {
+ templateKey = resetFieldValue
+ } else {
+ switch field.Type {
+ case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "string", "bool", "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64", "[]byte":
+ templateKey = resetFieldValue
+ }
+ }
+ if templateKey != -1 {
+ code, err := expandFieldTemplate(templateKey, field)
+ if err != nil {
+ return nil, err
+ }
+ fieldReset = append(fieldReset, code)
+ }
+ }
+ return fieldReset, nil
+}
+
+func (s *Struct) generateFieldDecoding(fields []*toolbox.FieldInfo) (string, []string, error) {
+ fieldCases := []string{}
+ var initCode = ""
+ for i := range fields {
+ if isSkipable(s.options, fields[i]) {
+ continue
+ }
+ var templateKey = -1
+ fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName))
+ field, err := NewField(s, fields[i], fieldTypeInfo)
+ if err != nil {
+ return "", nil, err
+ }
+ if fieldTypeInfo != nil {
+ if err = s.generateStructCode(fieldTypeInfo.Name); err != nil {
+ return "", nil, err
+ }
+ }
+
+ if field.IsAnonymous {
+ if fieldTypeInfo != nil {
+ if field.IsPointer {
+ init, err := expandBlockTemplate(embeddedStructInit, field)
+ if err != nil {
+ return "", nil, err
+ }
+ initCode += init
+ }
+ init, embeddedCases, err := s.generateFieldDecoding(fieldTypeInfo.Fields())
+ if err != nil {
+ return "", nil, err
+ }
+ initCode += init
+ fieldCases = append(fieldCases, embeddedCases...)
+ }
+ continue
+ }
+
+ main:
+ switch field.Type {
+ case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64":
+ templateKey = decodeBaseType
+ case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64":
+ templateKey = decodeBaseTypeSlice
+ s.generatePrimitiveArray(field)
+ case "[]byte":
+ templateKey = decodeRawType
+ default:
+
+ if fieldTypeInfo != nil {
+ if !(field.IsSlice || fieldTypeInfo.IsSlice) {
+
+ templateKey = decodeStruct
+ break main
+ }
+
+ switch fieldTypeInfo.ComponentType {
+ case "byte":
+ templateKey = decodeRawType
+ break main
+
+ case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64":
+ s.generatePrimitiveArray(field)
+ templateKey = decodeBaseTypeSlice
+ break main
+
+ }
+
+ if err = s.generateStructCode(field.ComponentType); err != nil {
+ return "", nil, err
+ }
+
+ templateKey = decodeStructSlice
+ if err = s.generateObjectArray(field); err != nil {
+ return "", nil, err
+ }
+
+ break main
+ } else if field.IsSlice {
+ templateKey = decodeStructSlice
+ if err = s.generateObjectArray(field); err != nil {
+ return "", nil, err
+ }
+
+ } else if strings.HasSuffix(field.Type, "time.Time") {
+ templateKey = decodeTime
+ s.addImport("time")
+ } else {
+ templateKey = decodeUnknown
+ }
+ }
+ if templateKey != -1 {
+ decodingCase, err := expandFieldTemplate(templateKey, field)
+ if err != nil {
+ return "", nil, err
+ }
+ fieldCases = append(fieldCases, decodingCase)
+ }
+
+ }
+ return initCode, fieldCases, nil
+}
+
+func (s *Struct) generateEmbeddedFieldEncoding(field *Field, fieldTypeInfo *toolbox.TypeInfo) ([]string, error) {
+ var result = []string{}
+ if fieldTypeInfo != nil {
+ embeddedCases, err := s.generateFieldEncoding(fieldTypeInfo.Fields())
+ if err != nil {
+ return nil, err
+ }
+ if field.IsPointer {
+ result = append(result, fmt.Sprintf(" if %v != nil {", field.Accessor))
+ for _, code := range embeddedCases {
+ result = append(result, " "+code)
+ }
+ result = append(result, " }")
+ } else {
+ result = append(result, embeddedCases...)
+ }
+ }
+ return result, nil
+}
+
+func (s *Struct) generateFieldEncoding(fields []*toolbox.FieldInfo) ([]string, error) {
+ fieldCases := []string{}
+ for i := range fields {
+ if isSkipable(s.options, fields[i]) {
+ continue
+ }
+ var templateKey = -1
+ fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName))
+ field, err := NewField(s, fields[i], fieldTypeInfo)
+ if err != nil {
+ return nil, err
+ }
+ if field.IsAnonymous {
+ embedded, err := s.generateEmbeddedFieldEncoding(field, fieldTypeInfo)
+ if err != nil {
+ return nil, err
+ }
+ fieldCases = append(fieldCases, embedded...)
+ continue
+ }
+ main:
+ switch field.Type {
+ case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64":
+ templateKey = encodeBaseType
+ case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64":
+ templateKey = encodeBaseTypeSlice
+ s.generatePrimitiveArray(field)
+ case "[]byte":
+ templateKey = encodeRawType
+ default:
+ if fieldTypeInfo != nil {
+ if !(field.IsSlice || fieldTypeInfo.IsSlice) {
+ templateKey = encodeStruct
+ break main
+ }
+ switch fieldTypeInfo.ComponentType {
+ case "byte":
+ templateKey = encodeRawType
+ break main
+ case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64":
+ templateKey = decodeBaseTypeSlice
+ break main
+ }
+ templateKey = encodeStructSlice
+ break main
+ } else if field.IsSlice {
+ templateKey = encodeStructSlice
+ } else if strings.HasSuffix(field.Type, "time.Time") {
+ templateKey = encodeTime
+ } else {
+ templateKey = encodeUnknown
+ }
+ }
+ if templateKey != -1 {
+ decodingCase, err := expandFieldTemplate(templateKey, field)
+ if err != nil {
+ return nil, err
+ }
+ fieldCases = append(fieldCases, decodingCase)
+ }
+
+ }
+ return fieldCases, nil
+}
+
+func NewStruct(info *toolbox.TypeInfo, generator *Generator) *Struct {
+ return &Struct{
+ TypeInfo: info,
+ Generator: generator,
+ Alias: extractReceiverAlias(info.Name),
+ }
+}
diff --git a/codegen/template.go b/codegen/template.go
@@ -0,0 +1,272 @@
+package codegen
+
+import (
+ "bytes"
+ "fmt"
+ "text/template"
+)
+
+const (
+ decodeBaseType = iota
+ encodeBaseType
+ decodeBaseTypeSlice
+ encodeBaseTypeSlice
+ decodeRawType
+ encodeRawType
+ decodeStruct
+ encodeStruct
+
+ decodeStructSlice
+ encodeStructSlice
+ decodeTime
+ encodeTime
+
+ decodeUnknown
+ encodeUnknown
+
+ resetFieldValue
+ poolInstanceRelease
+ poolSliceInstanceRelease
+)
+
+var fieldTemplate = map[int]string{
+ decodeBaseType: ` case "{{.Key}}":
+{{if .IsPointer}} var value {{.Type}}
+ err := dec.{{.DecodingMethod}}(&value)
+ if err == nil {
+ {{.Accessor}} = &value
+ }
+ return err
+{{else}} return dec.{{.DecodingMethod}}(&{{.Accessor}}){{end}}
+`,
+ encodeBaseType: ` enc.{{.EncodingMethod}}Key{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Accessor}})`,
+
+ decodeBaseTypeSlice: ` case "{{.Key}}":
+ var aSlice = {{.HelperType}}{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ {{.Mutator}} = {{.RawType}}(aSlice)
+ }
+ return err
+`,
+ encodeBaseTypeSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}})
+ enc.ArrayKey{{.OmitEmpty}}("{{.Key}}",{{.Var}}Slice)`,
+
+ decodeRawType: ` case "{{.Key}}":
+ var value = gojay.EmbeddedJSON{}
+ err := dec.AddEmbeddedJSON(&value)
+ if err == nil && len(value) > 0 {
+ {{.Mutator}} = {{.Type}}(value)
+ }
+ return err
+`,
+
+ encodeRawType: ` var {{.Var}}Slice = gojay.EmbeddedJSON({{.Accessor}})
+ enc.AddEmbeddedJSONKey{{.OmitEmpty}}("{{.Key}}", &{{.Var}}Slice)`,
+ decodeStruct: ` case "{{.Key}}":{{if .IsPointer}}
+ var value = {{.Init}}
+ err := dec.Object(value)
+ if err == nil {
+ {{.Mutator}} = value
+ }
+{{else}}
+ err := dec.Object(&{{.Mutator}})
+{{end}}
+ return err
+`,
+ encodeStruct: ` enc.ObjectKey{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}})`,
+
+ decodeStructSlice: ` case "{{.Key}}":
+ var aSlice = {{.HelperType}}{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ {{.Mutator}} = {{.RawType}}(aSlice)
+ }
+ return err
+ `,
+
+ encodeStructSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}})
+ enc.ArrayKey{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Var}}Slice)`,
+
+ decodeTime: ` case "{{.Key}}":
+ var format = {{.TimeLayout}}
+ var value = {{.Init}}
+ err := dec.DecodeTime({{.PointerModifier}}value, format){{if .IsPointer}}
+ if err == nil {
+ {{.Mutator}} = value
+ }{{end}}
+ return err
+`,
+
+ encodeTime: `{{if .IsPointer}} if {{.Accessor}} != nil {
+ enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}})
+ }{{else}} enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}){{end}}`,
+ decodeUnknown: ` case "{{.Key}}":
+ //TODO
+ //dec.Any({{.Accessor}})
+ return nil
+`,
+ encodeUnknown: `
+ //TODO
+ //enc.Any({{.Accessor}})
+
+`,
+ resetFieldValue: `{{if .ResetDependency}}{{.ResetDependency}}
+{{end}} {{.Mutator}} = {{.Reset}}`,
+ poolInstanceRelease: ` {{.PoolName}}.Put({{.Accessor}})`,
+
+ poolSliceInstanceRelease: ` for i := range {{.Accessor}} {
+ {{.Accessor}}[i].Reset()
+ {{.PoolName}}.Put({{.PointerModifier}}{{.Accessor}}[i])
+ }`,
+}
+
+const (
+ fileCode = iota
+ encodingStructType
+ baseTypeSlice
+ structTypeSlice
+ resetStruct
+ poolVar
+ poolInit
+ embeddedStructInit
+)
+
+var blockTemplate = map[int]string{
+ fileCode: `// Code generated by GoJayGen. DO NOT EDIT.\n\n
+package {{.Pkg}}
+
+import (
+{{.Imports}}
+)
+
+{{if .Init}}
+func init() {
+{{.Init}}
+}
+{{end}}
+
+{{.Code}}
+
+`,
+ encodingStructType: `
+
+//MarshalJSONObject implements MarshalerJSONObject
+func ({{.Receiver}}) MarshalJSONObject(enc *gojay.Encoder) {
+{{.EncodingCases}}
+}
+
+//IsNil checks if instance is nil
+func ({{.Receiver}}) IsNil() bool {
+ return {{.Alias}} == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func ({{.Receiver}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+{{.InitEmbedded}}
+ switch k {
+{{.DecodingCases}}
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func ({{.Receiver}}) NKeys() int { return {{.FieldCount}} }
+
+{{.Reset}}
+
+`,
+
+ baseTypeSlice: `
+
+type {{.HelperType}} {{.RawType}}
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value {{.ComponentType}}
+ if err := dec.{{.DecodingMethod}}(&value); err != nil {
+ return err
+ }
+ *a = append(*a, {{.ComponentInitModifier}}value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.{{.EncodingMethod}}({{.ComponentDereferenceModifier}}item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a {{.HelperType}}) IsNil() bool {
+ return len(a) == 0
+}
+`,
+
+ structTypeSlice: `
+type {{.HelperType}} {{.RawType}}
+
+func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = {{.ComponentInit}}
+ if err := dec.Object({{.ComponentPointerModifier}}value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object({{.ComponentPointerModifier}}s[i])
+ }
+}
+
+
+func (s {{.HelperType}}) IsNil() bool {
+ return len(s) == 0
+}
+
+
+`,
+
+ resetStruct: `
+// Reset reset fields
+func ({{.Receiver}}) Reset() {
+{{.Reset}}
+}
+`,
+
+ poolVar: `var {{.PoolName}} *sync.Pool`,
+ poolInit: ` {{.PoolName}} = &sync.Pool {
+ New: func()interface{} {
+ return &{{.Type}}{}
+ },
+ }`,
+ embeddedStructInit: `if {{.Accessor}} == nil {
+ {{.Accessor}} = {{.Init}}
+ }`,
+}
+
+func expandTemplate(namespace string, dictionary map[int]string, key int, data interface{}) (string, error) {
+ var id = fmt.Sprintf("%v_%v", namespace, key)
+ textTemplate, ok := dictionary[key]
+ if !ok {
+ return "", fmt.Errorf("failed to lookup template for %v.%v", namespace, key)
+ }
+ temlate, err := template.New(id).Parse(textTemplate)
+ if err != nil {
+ return "", fmt.Errorf("fiailed to parse template %v %v, due to %v", namespace, key, err)
+ }
+ writer := new(bytes.Buffer)
+ err = temlate.Execute(writer, data)
+ return writer.String(), err
+}
+
+func expandFieldTemplate(key int, data interface{}) (string, error) {
+ return expandTemplate("fieldTemplate", fieldTemplate, key, data)
+}
+
+func expandBlockTemplate(key int, data interface{}) (string, error) {
+ return expandTemplate("blockTemplate", blockTemplate, key, data)
+}
diff --git a/codegen/template_test.go b/codegen/template_test.go
@@ -0,0 +1,19 @@
+package codegen
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func Test_ExpandTemplate(t *testing.T) {
+ var dictionary = map[int]string{
+ 1: `type {{.TypeName}} {{.SourceTypeName}}`,
+ }
+ expaded, err := expandTemplate("test", dictionary, 1, struct {
+ TypeName string
+ SourceTypeName string
+ }{"A", "B"})
+
+ assert.Nil(t, err)
+ assert.Equal(t, "type A B", expaded)
+}
diff --git a/codegen/test/annotated_struct/encoding.go b/codegen/test/annotated_struct/encoding.go
@@ -0,0 +1,307 @@
+// Code generated by GoJayGen. DO NOT EDIT.\n\n
+package annotated_struct
+
+import (
+"github.com/francoispqt/gojay"
+"time"
+)
+
+
+
+
+
+
+type Ints []int
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value int
+ if err := dec.Int(&value); err != nil {
+ return err
+ }
+ *a = append(*a, value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Ints) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Int(item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Ints) IsNil() bool {
+ return len(a) == 0
+}
+
+
+
+type Float32sPtr []*float32
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value float32
+ if err := dec.Float32(&value); err != nil {
+ return err
+ }
+ *a = append(*a, &value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Float32(*item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Float32sPtr) IsNil() bool {
+ return len(a) == 0
+}
+
+
+type SubMessagesPtr []*SubMessage
+
+func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = &SubMessage{}
+ if err := dec.Object(value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(s[i])
+ }
+}
+
+
+func (s SubMessagesPtr) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+type SubMessages []SubMessage
+
+func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = SubMessage{}
+ if err := dec.Object(&value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(&s[i])
+ }
+}
+
+
+func (s SubMessages) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("id", m.Id)
+ enc.StringKey("description", m.Description)
+ enc.TimeKey("startDate", &m.StartTime, "2006-01-02 15:04:05")
+ if m.EndTime != nil {
+ enc.TimeKey("endDate", m.EndTime, "2006-01-02 15:04:05")
+ }
+}
+
+//IsNil checks if instance is nil
+func (m *SubMessage) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "id":
+ return dec.Int(&m.Id)
+
+ case "description":
+ return dec.String(&m.Description)
+
+ case "startDate":
+ var format = "2006-01-02 15:04:05"
+ var value = time.Time{}
+ err := dec.DecodeTime(&value, format)
+ return err
+
+ case "endDate":
+ var format = "2006-01-02 15:04:05"
+ var value = &time.Time{}
+ err := dec.DecodeTime(value, format)
+ if err == nil {
+ m.EndTime = value
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *SubMessage) NKeys() int { return 4 }
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (p *Paylod) MarshalJSONObject(enc *gojay.Encoder) {
+
+}
+
+//IsNil checks if instance is nil
+func (p *Paylod) IsNil() bool {
+ return p == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (p *Paylod) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (p *Paylod) NKeys() int { return 0 }
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *Message) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("id", m.Id)
+ enc.StringKey("name", m.Name)
+ enc.Float64Key("price", m.Price)
+ var intsSlice = Ints(m.Ints)
+ enc.ArrayKey("ints",intsSlice)
+ var floatsSlice = Float32sPtr(m.Floats)
+ enc.ArrayKey("floats",floatsSlice)
+ enc.ObjectKey("subMessageX", m.SubMessageX)
+ var messagesXSlice = SubMessagesPtr(m.MessagesX)
+ enc.ArrayKey("messagesX", messagesXSlice)
+ enc.ObjectKey("SubMessageY", &m.SubMessageY)
+ var messagesYSlice = SubMessages(m.MessagesY)
+ enc.ArrayKey("MessagesY", messagesYSlice)
+ enc.BoolKey("enabled", *m.IsTrue)
+ var payloadSlice = gojay.EmbeddedJSON(m.Payload)
+ enc.AddEmbeddedJSONKey("data", &payloadSlice)
+}
+
+//IsNil checks if instance is nil
+func (m *Message) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "id":
+ return dec.Int(&m.Id)
+
+ case "name":
+ return dec.String(&m.Name)
+
+ case "price":
+ return dec.Float64(&m.Price)
+
+ case "ints":
+ var aSlice = Ints{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Ints = []int(aSlice)
+ }
+ return err
+
+ case "floats":
+ var aSlice = Float32sPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Floats = []*float32(aSlice)
+ }
+ return err
+
+ case "subMessageX":
+ var value = &SubMessage{}
+ err := dec.Object(value)
+ if err == nil {
+ m.SubMessageX = value
+ }
+
+ return err
+
+ case "messagesX":
+ var aSlice = SubMessagesPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesX = []*SubMessage(aSlice)
+ }
+ return err
+
+ case "SubMessageY":
+ err := dec.Object(&m.SubMessageY)
+
+ return err
+
+ case "MessagesY":
+ var aSlice = SubMessages{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesY = []SubMessage(aSlice)
+ }
+ return err
+
+ case "enabled":
+ var value bool
+ err := dec.Bool(&value)
+ if err == nil {
+ m.IsTrue = &value
+ }
+ return err
+
+
+ case "data":
+ var value = gojay.EmbeddedJSON{}
+ err := dec.AddEmbeddedJSON(&value)
+ if err == nil && len(value) > 0 {
+ m.Payload = Paylod(value)
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *Message) NKeys() int { return 11 }
+
+
+
+
+
diff --git a/codegen/test/annotated_struct/encoding_test.go b/codegen/test/annotated_struct/encoding_test.go
@@ -0,0 +1,216 @@
+package annotated_struct
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/francoispqt/gojay"
+ "github.com/viant/assertly"
+ "log"
+ "bytes"
+)
+
+func TestMessage_Unmarshal(t *testing.T) {
+
+
+
+ input := `{
+ "id": 1022,
+ "name": "name acc",
+ "price": 13.3,
+ "ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "subMessageX": {
+ "id": 102,
+ "description": "abcd"
+ },
+ "messagesX": [
+ {
+ "id": 2102,
+ "description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "id": 3102,
+ "description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "id": 5102,
+ "description": "abcg"
+ },
+ {
+ "id": 5106,
+ "description": "abcgg"
+ }
+ ],
+ "enabled": true,
+ "data": "123"
+}`
+
+
+ expacted := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": "\"123\""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ if !assert.Nil(t, err) {
+ log.Fatal(err)
+ }
+ assertly.AssertValues(t, expacted, message)
+}
+
+
+
+
+func TestMessage_Marshal(t *testing.T) {
+
+ input := `{
+ "id": 1022,
+ "name": "name acc",
+ "price": 13.3,
+ "ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "subMessageX": {
+ "id": 102,
+ "description": "abcd"
+ },
+ "messagesX": [
+ {
+ "id": 2102,
+ "description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "id": 3102,
+ "description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "id": 5102,
+ "description": "abcg"
+ },
+ {
+ "id": 5106,
+ "description": "abcgg"
+ }
+ ],
+ "enabled": true,
+ "data": "123"
+}`
+
+
+ expacted := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": "\"123\""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ if !assert.Nil(t, err) {
+ log.Fatal(err)
+ }
+ assertly.AssertValues(t, expacted, message)
+ var writer = new(bytes.Buffer)
+ encoder := gojay.NewEncoder(writer)
+ err = encoder.Encode(message)
+ assert.Nil(t, err)
+ var JSON = writer.String()
+ assertly.AssertValues(t, input, JSON)
+
+}
+\ No newline at end of file
diff --git a/codegen/test/annotated_struct/message.go b/codegen/test/annotated_struct/message.go
@@ -0,0 +1,19 @@
+package annotated_struct
+
+
+type Paylod []byte
+
+type Message struct {
+ Id int `json:"id"`
+ Name string `json:"name"`
+ Price float64 `json:"price"`
+ Ints []int `json:"ints"`
+ Floats []*float32 `json:"floats"`
+ SubMessageX *SubMessage `json:"subMessageX"`
+ MessagesX []*SubMessage `json:"messagesX"`
+ SubMessageY SubMessage
+ MessagesY []SubMessage
+ IsTrue *bool `json:"enabled"`
+ Payload Paylod `json:"data"`
+ Ignore string `json:"-"`
+}
diff --git a/codegen/test/annotated_struct/sub_message.go b/codegen/test/annotated_struct/sub_message.go
@@ -0,0 +1,10 @@
+package annotated_struct
+
+import "time"
+
+type SubMessage struct {
+ Id int `json:"id"`
+ Description string `json:"description"`
+ StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"`
+ EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"`
+}
diff --git a/codegen/test/basic_struct/encoding.go b/codegen/test/basic_struct/encoding.go
@@ -0,0 +1,280 @@
+// Code generated by GoJayGen. DO NOT EDIT.\n\n
+package basic_struct
+
+import (
+"github.com/francoispqt/gojay"
+"time"
+)
+
+
+
+
+
+
+type Ints []int
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value int
+ if err := dec.Int(&value); err != nil {
+ return err
+ }
+ *a = append(*a, value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Ints) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Int(item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Ints) IsNil() bool {
+ return len(a) == 0
+}
+
+
+
+type Float32sPtr []*float32
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value float32
+ if err := dec.Float32(&value); err != nil {
+ return err
+ }
+ *a = append(*a, &value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Float32(*item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Float32sPtr) IsNil() bool {
+ return len(a) == 0
+}
+
+
+type SubMessagesPtr []*SubMessage
+
+func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = &SubMessage{}
+ if err := dec.Object(value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(s[i])
+ }
+}
+
+
+func (s SubMessagesPtr) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+type SubMessages []SubMessage
+
+func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = SubMessage{}
+ if err := dec.Object(&value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(&s[i])
+ }
+}
+
+
+func (s SubMessages) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("Id", m.Id)
+ enc.StringKey("Description", m.Description)
+ enc.TimeKey("StartTime", &m.StartTime, time.RFC3339)
+ if m.EndTime != nil {
+ enc.TimeKey("EndTime", m.EndTime, time.RFC3339)
+ }
+}
+
+//IsNil checks if instance is nil
+func (m *SubMessage) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Id":
+ return dec.Int(&m.Id)
+
+ case "Description":
+ return dec.String(&m.Description)
+
+ case "StartTime":
+ var format = time.RFC3339
+ var value = time.Time{}
+ err := dec.DecodeTime(&value, format)
+ return err
+
+ case "EndTime":
+ var format = time.RFC3339
+ var value = &time.Time{}
+ err := dec.DecodeTime(value, format)
+ if err == nil {
+ m.EndTime = value
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *SubMessage) NKeys() int { return 4 }
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *Message) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("Id", m.Id)
+ enc.StringKey("Name", m.Name)
+ enc.Float64Key("Price", m.Price)
+ var intsSlice = Ints(m.Ints)
+ enc.ArrayKey("Ints",intsSlice)
+ var floatsSlice = Float32sPtr(m.Floats)
+ enc.ArrayKey("Floats",floatsSlice)
+ enc.ObjectKey("SubMessageX", m.SubMessageX)
+ var messagesXSlice = SubMessagesPtr(m.MessagesX)
+ enc.ArrayKey("MessagesX", messagesXSlice)
+ enc.ObjectKey("SubMessageY", &m.SubMessageY)
+ var messagesYSlice = SubMessages(m.MessagesY)
+ enc.ArrayKey("MessagesY", messagesYSlice)
+ enc.BoolKey("IsTrue", *m.IsTrue)
+ var payloadSlice = gojay.EmbeddedJSON(m.Payload)
+ enc.AddEmbeddedJSONKey("Payload", &payloadSlice)
+}
+
+//IsNil checks if instance is nil
+func (m *Message) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Id":
+ return dec.Int(&m.Id)
+
+ case "Name":
+ return dec.String(&m.Name)
+
+ case "Price":
+ return dec.Float64(&m.Price)
+
+ case "Ints":
+ var aSlice = Ints{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Ints = []int(aSlice)
+ }
+ return err
+
+ case "Floats":
+ var aSlice = Float32sPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Floats = []*float32(aSlice)
+ }
+ return err
+
+ case "SubMessageX":
+ var value = &SubMessage{}
+ err := dec.Object(value)
+ if err == nil {
+ m.SubMessageX = value
+ }
+
+ return err
+
+ case "MessagesX":
+ var aSlice = SubMessagesPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesX = []*SubMessage(aSlice)
+ }
+ return err
+
+ case "SubMessageY":
+ err := dec.Object(&m.SubMessageY)
+
+ return err
+
+ case "MessagesY":
+ var aSlice = SubMessages{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesY = []SubMessage(aSlice)
+ }
+ return err
+
+ case "IsTrue":
+ var value bool
+ err := dec.Bool(&value)
+ if err == nil {
+ m.IsTrue = &value
+ }
+ return err
+
+
+ case "Payload":
+ var value = gojay.EmbeddedJSON{}
+ err := dec.AddEmbeddedJSON(&value)
+ if err == nil && len(value) > 0 {
+ m.Payload = []byte(value)
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *Message) NKeys() int { return 11 }
+
+
+
+
+
diff --git a/codegen/test/basic_struct/encoding_test.go b/codegen/test/basic_struct/encoding_test.go
@@ -0,0 +1,122 @@
+package basic_struct
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/francoispqt/gojay"
+ "github.com/viant/assertly"
+ "bytes"
+)
+
+func TestMessage_Unmarshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+ assertly.AssertValues(t, input, message)
+}
+
+
+
+func TestMessage_Marshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+
+ var writer = new(bytes.Buffer)
+
+ encoder := gojay.NewEncoder(writer)
+ err = encoder.Encode(message)
+ assert.Nil(t, err)
+ var JSON = writer.String()
+ assertly.AssertValues(t, input, JSON)
+}
diff --git a/codegen/test/basic_struct/message.go b/codegen/test/basic_struct/message.go
@@ -0,0 +1,18 @@
+package basic_struct
+
+
+
+
+type Message struct {
+ Id int
+ Name string
+ Price float64
+ Ints []int
+ Floats []*float32
+ SubMessageX *SubMessage
+ MessagesX []*SubMessage
+ SubMessageY SubMessage
+ MessagesY []SubMessage
+ IsTrue *bool
+ Payload []byte
+}
diff --git a/codegen/test/basic_struct/sub_message.go b/codegen/test/basic_struct/sub_message.go
@@ -0,0 +1,10 @@
+package basic_struct
+
+import "time"
+
+type SubMessage struct {
+ Id int
+ Description string
+ StartTime time.Time
+ EndTime *time.Time
+}
diff --git a/codegen/test/embedded_struct/encoding.go b/codegen/test/embedded_struct/encoding.go
@@ -0,0 +1,336 @@
+// Code generated by GoJayGen. DO NOT EDIT.\n\n
+package embedded_struct
+
+import (
+"github.com/francoispqt/gojay"
+"time"
+)
+
+
+
+
+
+type SubMessagesPtr []*SubMessage
+
+func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = &SubMessage{}
+ if err := dec.Object(value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(s[i])
+ }
+}
+
+
+func (s SubMessagesPtr) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+type SubMessages []SubMessage
+
+func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = SubMessage{}
+ if err := dec.Object(&value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(&s[i])
+ }
+}
+
+
+func (s SubMessages) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+
+type Ints []int
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value int
+ if err := dec.Int(&value); err != nil {
+ return err
+ }
+ *a = append(*a, value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Ints) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Int(item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Ints) IsNil() bool {
+ return len(a) == 0
+}
+
+
+
+type Float32sPtr []*float32
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value float32
+ if err := dec.Float32(&value); err != nil {
+ return err
+ }
+ *a = append(*a, &value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Float32(*item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Float32sPtr) IsNil() bool {
+ return len(a) == 0
+}
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (i *BaseId) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("Id", i.Id)
+ enc.StringKey("Name", i.Name)
+}
+
+//IsNil checks if instance is nil
+func (i *BaseId) IsNil() bool {
+ return i == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (i *BaseId) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Id":
+ return dec.Int(&i.Id)
+
+ case "Name":
+ return dec.String(&i.Name)
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (i *BaseId) NKeys() int { return 2 }
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.StringKey("Description", m.Description)
+ enc.TimeKey("StartTime", &m.StartTime, time.RFC3339)
+ if m.EndTime != nil {
+ enc.TimeKey("EndTime", m.EndTime, time.RFC3339)
+ }
+}
+
+//IsNil checks if instance is nil
+func (m *SubMessage) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Description":
+ return dec.String(&m.Description)
+
+ case "StartTime":
+ var format = time.RFC3339
+ var value = time.Time{}
+ err := dec.DecodeTime(&value, format)
+ return err
+
+ case "EndTime":
+ var format = time.RFC3339
+ var value = &time.Time{}
+ err := dec.DecodeTime(value, format)
+ if err == nil {
+ m.EndTime = value
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *SubMessage) NKeys() int { return 3 }
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *Message) MarshalJSONObject(enc *gojay.Encoder) {
+ if m.BaseId != nil {
+ enc.IntKey("Id", m.Id)
+ enc.StringKey("Name", m.Name)
+ }
+ enc.StringKey("Description", m.Description)
+ enc.TimeKey("StartTime", &m.StartTime, time.RFC3339)
+ if m.EndTime != nil {
+ enc.TimeKey("EndTime", m.EndTime, time.RFC3339)
+ }
+ enc.Float64Key("Price", m.Price)
+ var intsSlice = Ints(m.Ints)
+ enc.ArrayKey("Ints",intsSlice)
+ var floatsSlice = Float32sPtr(m.Floats)
+ enc.ArrayKey("Floats",floatsSlice)
+ enc.ObjectKey("SubMessageX", m.SubMessageX)
+ var messagesXSlice = SubMessagesPtr(m.MessagesX)
+ enc.ArrayKey("MessagesX", messagesXSlice)
+ enc.ObjectKey("SubMessageY", &m.SubMessageY)
+ var messagesYSlice = SubMessages(m.MessagesY)
+ enc.ArrayKey("MessagesY", messagesYSlice)
+ enc.BoolKey("IsTrue", *m.IsTrue)
+ var payloadSlice = gojay.EmbeddedJSON(m.Payload)
+ enc.AddEmbeddedJSONKey("Payload", &payloadSlice)
+}
+
+//IsNil checks if instance is nil
+func (m *Message) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+if m.BaseId == nil {
+ m.BaseId = &BaseId{}
+ }
+ switch k {
+ case "Id":
+ return dec.Int(&m.Id)
+
+ case "Name":
+ return dec.String(&m.Name)
+
+ case "Description":
+ return dec.String(&m.Description)
+
+ case "StartTime":
+ var format = time.RFC3339
+ var value = time.Time{}
+ err := dec.DecodeTime(&value, format)
+ return err
+
+ case "EndTime":
+ var format = time.RFC3339
+ var value = &time.Time{}
+ err := dec.DecodeTime(value, format)
+ if err == nil {
+ m.EndTime = value
+ }
+ return err
+
+ case "Price":
+ return dec.Float64(&m.Price)
+
+ case "Ints":
+ var aSlice = Ints{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Ints = []int(aSlice)
+ }
+ return err
+
+ case "Floats":
+ var aSlice = Float32sPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Floats = []*float32(aSlice)
+ }
+ return err
+
+ case "SubMessageX":
+ var value = &SubMessage{}
+ err := dec.Object(value)
+ if err == nil {
+ m.SubMessageX = value
+ }
+
+ return err
+
+ case "MessagesX":
+ var aSlice = SubMessagesPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesX = []*SubMessage(aSlice)
+ }
+ return err
+
+ case "SubMessageY":
+ err := dec.Object(&m.SubMessageY)
+
+ return err
+
+ case "MessagesY":
+ var aSlice = SubMessages{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesY = []SubMessage(aSlice)
+ }
+ return err
+
+ case "IsTrue":
+ var value bool
+ err := dec.Bool(&value)
+ if err == nil {
+ m.IsTrue = &value
+ }
+ return err
+
+
+ case "Payload":
+ var value = gojay.EmbeddedJSON{}
+ err := dec.AddEmbeddedJSON(&value)
+ if err == nil && len(value) > 0 {
+ m.Payload = []byte(value)
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *Message) NKeys() int { return 14 }
+
+
+
+
+
diff --git a/codegen/test/embedded_struct/encoding_test.go b/codegen/test/embedded_struct/encoding_test.go
@@ -0,0 +1,108 @@
+package embedded_struct
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/francoispqt/gojay"
+ "github.com/viant/assertly"
+ "bytes"
+)
+
+func TestMessage_Unmarshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Description": "abcd",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "MessagesX": [
+ {
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Description": "abcg"
+ },
+ {
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+ assertly.AssertValues(t, input, message)
+}
+
+
+
+func TestMessage_Marshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Description": "abcd",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "MessagesX": [
+ {
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Description": "abcg"
+ },
+ {
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var err error
+ var data = []byte(input)
+ message := &Message{}
+ err = gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+ assertly.AssertValues(t, input, message)
+ var writer = new(bytes.Buffer)
+
+ encoder := gojay.NewEncoder(writer)
+ err = encoder.Encode(message)
+ assert.Nil(t, err)
+ var JSON = writer.String()
+ assertly.AssertValues(t, input, JSON)
+}
+\ No newline at end of file
diff --git a/codegen/test/embedded_struct/message.go b/codegen/test/embedded_struct/message.go
@@ -0,0 +1,22 @@
+package embedded_struct
+
+
+type BaseId struct {
+ Id int
+ Name string
+
+}
+
+type Message struct {
+ *BaseId
+ SubMessage
+ Price float64
+ Ints []int
+ Floats []*float32
+ SubMessageX *SubMessage
+ MessagesX []*SubMessage
+ SubMessageY SubMessage
+ MessagesY []SubMessage
+ IsTrue *bool
+ Payload []byte
+}
diff --git a/codegen/test/embedded_struct/sub_message.go b/codegen/test/embedded_struct/sub_message.go
@@ -0,0 +1,9 @@
+package embedded_struct
+
+import "time"
+
+type SubMessage struct {
+ Description string
+ StartTime time.Time
+ EndTime *time.Time
+}
diff --git a/codegen/test/pooled_struct/encoding.go b/codegen/test/pooled_struct/encoding.go
@@ -0,0 +1,326 @@
+// Code generated by GoJayGen. DO NOT EDIT.\n\n
+package pooled_struct
+
+import (
+"sync"
+"time"
+"github.com/francoispqt/gojay"
+)
+
+
+func init() {
+ MessagePool = &sync.Pool {
+ New: func()interface{} {
+ return &Message{}
+ },
+ }
+ SubMessagePool = &sync.Pool {
+ New: func()interface{} {
+ return &SubMessage{}
+ },
+ }
+}
+
+
+var MessagePool *sync.Pool
+var SubMessagePool *sync.Pool
+
+
+
+type Ints []int
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value int
+ if err := dec.Int(&value); err != nil {
+ return err
+ }
+ *a = append(*a, value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Ints) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Int(item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Ints) IsNil() bool {
+ return len(a) == 0
+}
+
+
+
+type Float32sPtr []*float32
+
+//UnmarshalJSONArray decodes JSON array elements into slice
+func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value float32
+ if err := dec.Float32(&value); err != nil {
+ return err
+ }
+ *a = append(*a, &value)
+ return nil
+}
+
+//MarshalJSONArray encodes arrays into JSON
+func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, item := range a {
+ enc.Float32(*item)
+ }
+}
+
+//IsNil checks if array is nil
+func (a Float32sPtr) IsNil() bool {
+ return len(a) == 0
+}
+
+
+type SubMessagesPtr []*SubMessage
+
+func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = &SubMessage{}
+ if err := dec.Object(value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(s[i])
+ }
+}
+
+
+func (s SubMessagesPtr) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+type SubMessages []SubMessage
+
+func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var value = SubMessage{}
+ if err := dec.Object(&value); err != nil {
+ return err
+ }
+ *s = append(*s, value)
+ return nil
+}
+
+func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) {
+ for i := range s {
+ enc.Object(&s[i])
+ }
+}
+
+
+func (s SubMessages) IsNil() bool {
+ return len(s) == 0
+}
+
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("Id", m.Id)
+ enc.StringKey("Description", m.Description)
+ enc.TimeKey("StartTime", &m.StartTime, time.RFC3339)
+ if m.EndTime != nil {
+ enc.TimeKey("EndTime", m.EndTime, time.RFC3339)
+ }
+}
+
+//IsNil checks if instance is nil
+func (m *SubMessage) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Id":
+ return dec.Int(&m.Id)
+
+ case "Description":
+ return dec.String(&m.Description)
+
+ case "StartTime":
+ var format = time.RFC3339
+ var value = time.Time{}
+ err := dec.DecodeTime(&value, format)
+ return err
+
+ case "EndTime":
+ var format = time.RFC3339
+ var value = &time.Time{}
+ err := dec.DecodeTime(value, format)
+ if err == nil {
+ m.EndTime = value
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *SubMessage) NKeys() int { return 4 }
+
+
+// Reset reset fields
+func (m *SubMessage) Reset() {
+ m.Id = 0
+ m.Description = ""
+ m.EndTime = nil
+}
+
+
+
+
+
+//MarshalJSONObject implements MarshalerJSONObject
+func (m *Message) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("Id", m.Id)
+ enc.StringKey("Name", m.Name)
+ enc.Float64Key("Price", m.Price)
+ var intsSlice = Ints(m.Ints)
+ enc.ArrayKey("Ints",intsSlice)
+ var floatsSlice = Float32sPtr(m.Floats)
+ enc.ArrayKey("Floats",floatsSlice)
+ enc.ObjectKey("SubMessageX", m.SubMessageX)
+ var messagesXSlice = SubMessagesPtr(m.MessagesX)
+ enc.ArrayKey("MessagesX", messagesXSlice)
+ enc.ObjectKey("SubMessageY", &m.SubMessageY)
+ var messagesYSlice = SubMessages(m.MessagesY)
+ enc.ArrayKey("MessagesY", messagesYSlice)
+ enc.BoolKey("IsTrue", *m.IsTrue)
+ var payloadSlice = gojay.EmbeddedJSON(m.Payload)
+ enc.AddEmbeddedJSONKey("Payload", &payloadSlice)
+}
+
+//IsNil checks if instance is nil
+func (m *Message) IsNil() bool {
+ return m == nil
+}
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+
+ switch k {
+ case "Id":
+ return dec.Int(&m.Id)
+
+ case "Name":
+ return dec.String(&m.Name)
+
+ case "Price":
+ return dec.Float64(&m.Price)
+
+ case "Ints":
+ var aSlice = Ints{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Ints = []int(aSlice)
+ }
+ return err
+
+ case "Floats":
+ var aSlice = Float32sPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.Floats = []*float32(aSlice)
+ }
+ return err
+
+ case "SubMessageX":
+ var value = SubMessagePool.Get().(*SubMessage)
+ err := dec.Object(value)
+ if err == nil {
+ m.SubMessageX = value
+ }
+
+ return err
+
+ case "MessagesX":
+ var aSlice = SubMessagesPtr{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesX = []*SubMessage(aSlice)
+ }
+ return err
+
+ case "SubMessageY":
+ err := dec.Object(&m.SubMessageY)
+
+ return err
+
+ case "MessagesY":
+ var aSlice = SubMessages{}
+ err := dec.Array(&aSlice)
+ if err == nil && len(aSlice) > 0 {
+ m.MessagesY = []SubMessage(aSlice)
+ }
+ return err
+
+ case "IsTrue":
+ var value bool
+ err := dec.Bool(&value)
+ if err == nil {
+ m.IsTrue = &value
+ }
+ return err
+
+
+ case "Payload":
+ var value = gojay.EmbeddedJSON{}
+ err := dec.AddEmbeddedJSON(&value)
+ if err == nil && len(value) > 0 {
+ m.Payload = []byte(value)
+ }
+ return err
+
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (m *Message) NKeys() int { return 11 }
+
+
+// Reset reset fields
+func (m *Message) Reset() {
+ m.Id = 0
+ m.Name = ""
+ m.Price = 0.0
+ m.Ints = nil
+ m.Floats = nil
+ SubMessagePool.Put(m.SubMessageX)
+ m.SubMessageX = nil
+ for i := range m.MessagesX {
+ m.MessagesX[i].Reset()
+ SubMessagePool.Put(m.MessagesX[i])
+ }
+ m.MessagesX = nil
+ for i := range m.MessagesY {
+ m.MessagesY[i].Reset()
+ SubMessagePool.Put(&m.MessagesY[i])
+ }
+ m.MessagesY = nil
+ m.IsTrue = nil
+ m.Payload = nil
+}
+
+
+
+
diff --git a/codegen/test/pooled_struct/encoding_test.go b/codegen/test/pooled_struct/encoding_test.go
@@ -0,0 +1,124 @@
+package pooled_struct
+
+import (
+ "testing"
+ "github.com/francoispqt/gojay"
+ "github.com/stretchr/testify/assert"
+ "github.com/viant/assertly"
+ "bytes"
+)
+
+func TestMessage_Unmarshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var data = []byte(input)
+ message := MessagePool.Get().(*Message)
+ err :=gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+ message.Reset()
+ MessagePool.Put(message)
+
+}
+
+
+func TestMessage_Marshal(t *testing.T) {
+
+ input := `{
+ "Id": 1022,
+ "Name": "name acc",
+ "Price": 13.3,
+ "Ints": [
+ 1,
+ 2,
+ 5
+ ],
+ "Floats": [
+ 2.3,
+ 4.6,
+ 7.4
+ ],
+ "SubMessageX": {
+ "Id": 102,
+ "Description": "abcd"
+ },
+ "MessagesX": [
+ {
+ "Id": 2102,
+ "Description": "abce"
+ }
+ ],
+ "SubMessageY": {
+ "Id": 3102,
+ "Description": "abcf"
+ },
+ "MessagesY": [
+ {
+ "Id": 5102,
+ "Description": "abcg"
+ },
+ {
+ "Id": 5106,
+ "Description": "abcgg"
+ }
+ ],
+ "IsTrue": true,
+ "Payload": ""
+}`
+
+ var data = []byte(input)
+ message := MessagePool.Get().(*Message)
+ err :=gojay.UnmarshalJSONObject(data, message)
+ assert.Nil(t, err)
+ defer func() {
+ message.Reset()
+ MessagePool.Put(message)
+
+ }()
+ var writer = new(bytes.Buffer)
+ encoder := gojay.NewEncoder(writer)
+ err = encoder.Encode(message)
+ assert.Nil(t, err)
+ var JSON = writer.String()
+ assertly.AssertValues(t, input, JSON)
+}
+\ No newline at end of file
diff --git a/codegen/test/pooled_struct/message.go b/codegen/test/pooled_struct/message.go
@@ -0,0 +1,18 @@
+package pooled_struct
+
+
+
+
+type Message struct {
+ Id int
+ Name string
+ Price float64
+ Ints []int
+ Floats []*float32
+ SubMessageX *SubMessage
+ MessagesX []*SubMessage
+ SubMessageY SubMessage
+ MessagesY []SubMessage
+ IsTrue *bool
+ Payload []byte
+}
diff --git a/codegen/test/pooled_struct/sub_message.go b/codegen/test/pooled_struct/sub_message.go
@@ -0,0 +1,10 @@
+package pooled_struct
+
+import "time"
+
+type SubMessage struct {
+ Id int
+ Description string
+ StartTime time.Time
+ EndTime *time.Time
+}
diff --git a/gojaygen/gojaygen b/gojaygen/gojaygen
Binary files differ.
diff --git a/gojaygen/gojaygen.go b/gojaygen/gojaygen.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "flag"
+ "github.com/adrianwit/gojay/codegen"
+ "log"
+)
+
+var dst = flag.String("o", "", "destination file to output generated implementations")
+var src = flag.String("s", "", "source dir or file (absolute or relative path)")
+var types = flag.String("t", "", "types to generate")
+var annotation = flag.String("a", "json", "annotation tagg")
+var poolObjects = flag.String("p", "", "generate code to reuse objects")
+
+func main() {
+ flag.Parse()
+ options := codegen.NewOptionsWithFlagSet(flag.CommandLine)
+ gen := &codegen.Generator{}
+ if err := gen.Generate(options); err != nil {
+ log.Fatal(err)
+ }
+}