diff --git a/encode_test.go b/encode_test.go index 5b10d75f..058b503d 100644 --- a/encode_test.go +++ b/encode_test.go @@ -2392,6 +2392,32 @@ func TestIssue324(t *testing.T) { } } +func TestCamelCaseField(t *testing.T) { + type T struct { + FieldA bool + } + + type T2 struct { + FieldA bool `json:"fieldA"` + } + + v := T{FieldA: true} + got, err := json.MarshalWithOption(v, json.EnableCamelCase()) + if err != nil { + t.Fatal(err) + } + + v2 := T2{FieldA: true} + expected, err := json.Marshal(v2) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(expected, got) { + t.Fatalf("failed to encode. expected %q but got %q", expected, got) + } +} + func TestIssue339(t *testing.T) { type T1 struct { *big.Int diff --git a/internal/encoder/option.go b/internal/encoder/option.go index 82d5ce3e..db3a4348 100644 --- a/internal/encoder/option.go +++ b/internal/encoder/option.go @@ -5,7 +5,7 @@ import ( "io" ) -type OptionFlag uint8 +type OptionFlag uint16 const ( HTMLEscapeOption OptionFlag = 1 << iota @@ -16,6 +16,7 @@ const ( ContextOption NormalizeUTF8Option FieldQueryOption + CamelCaseOption ) type Option struct { diff --git a/internal/encoder/vm/util.go b/internal/encoder/vm/util.go index 86291d7b..a1cb51c4 100644 --- a/internal/encoder/vm/util.go +++ b/internal/encoder/vm/util.go @@ -3,6 +3,7 @@ package vm import ( "encoding/json" "fmt" + "unicode" "unsafe" "github.com/goccy/go-json/internal/encoder" @@ -184,7 +185,17 @@ func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte { return append(b, '{') } -func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { +func appendStructKey(e *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { + if e.Option.Flag&encoder.CamelCaseOption > 0 { + key := []rune(code.Key) + for i := range key { + if unicode.IsLetter(key[i]) { + key[i] = unicode.ToLower(key[i]) + break + } + } + return append(b, string(key)...) + } return append(b, code.Key...) } diff --git a/option.go b/option.go index af400a45..bba4f747 100644 --- a/option.go +++ b/option.go @@ -24,6 +24,13 @@ func DisableHTMLEscape() EncodeOptionFunc { } } +// EnableCamelCase convert the keys to camel case when encoding a struct. +func EnableCamelCase() EncodeOptionFunc { + return func(opt *EncodeOption) { + opt.Flag |= encoder.CamelCaseOption + } +} + // DisableNormalizeUTF8 // By default, when encoding string, UTF8 characters in the range of 0x80 - 0xFF are processed by applying \ufffd for invalid code and escaping for \u2028 and \u2029. // This option disables this behaviour. You can expect faster speeds by applying this option, but be careful.