Skip to content

Commit 6c91989

Browse files
author
Tao Wen
committed
support struct tag
1 parent eb13cd5 commit 6c91989

File tree

5 files changed

+150
-1
lines changed

5 files changed

+150
-1
lines changed

gen/main.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,28 @@ func genStruct(structType *ast.StructType) {
210210
_l(" switch {")
211211
for _, field := range structType.Fields.List {
212212
fieldName := field.Names[0].Name
213+
encodedFieldName := fieldName
214+
if field.Tag != nil {
215+
t, _ := strconv.Unquote(field.Tag.Value)
216+
tags, err := parseStructTag(t)
217+
if err != nil {
218+
reportError(fmt.Errorf("%s: %w", t, err))
219+
return
220+
}
221+
jsonTag := tags["json"]
222+
if jsonTag != nil && len(jsonTag) > 0 {
223+
if jsonTag[0] == "-" {
224+
continue
225+
}
226+
encodedFieldName = jsonTag[0]
227+
}
228+
}
213229
isNotExported := unicode.IsLower(rune(fieldName[0])) || fieldName[0] == '_'
214230
if isNotExported {
215231
continue
216232
}
217233
ptr := fmt.Sprintf("&(*out).%s", fieldName)
218-
_f(" case field == `%s`:", fieldName)
234+
_f(" case field == `%s`:", encodedFieldName)
219235
genDecodeStmt(field.Type, ptr)
220236
}
221237
_l(" default:")

gen/structtag.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package main
2+
3+
// copied from https://github.com/fatih/structtag/blob/master/tags.go
4+
5+
import (
6+
"errors"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
var (
12+
errTagSyntax = errors.New("bad syntax for struct tag pair")
13+
errTagKeySyntax = errors.New("bad syntax for struct tag key")
14+
errTagValueSyntax = errors.New("bad syntax for struct tag value")
15+
16+
errKeyNotSet = errors.New("tag key does not exist")
17+
errTagNotExist = errors.New("tag does not exist")
18+
errTagKeyMismatch = errors.New("mismatch between key and tag.key")
19+
)
20+
21+
// parseStructTag parses a single struct field tag and returns the set of tags.
22+
func parseStructTag(tag string) (map[string][]string, error) {
23+
tags := map[string][]string{}
24+
25+
hasTag := tag != ""
26+
27+
// NOTE(arslan) following code is from reflect and vet package with some
28+
// modifications to collect all necessary information and extend it with
29+
// usable methods
30+
for tag != "" {
31+
// Skip leading space.
32+
i := 0
33+
for i < len(tag) && tag[i] == ' ' {
34+
i++
35+
}
36+
tag = tag[i:]
37+
if tag == "" {
38+
break
39+
}
40+
41+
// Scan to colon. A space, a quote or a control character is a syntax
42+
// error. Strictly speaking, control chars include the range [0x7f,
43+
// 0x9f], not just [0x00, 0x1f], but in practice, we ignore the
44+
// multi-byte control characters as it is simpler to inspect the tag's
45+
// bytes than the tag's runes.
46+
i = 0
47+
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
48+
i++
49+
}
50+
51+
if i == 0 {
52+
return nil, errTagKeySyntax
53+
}
54+
if i+1 >= len(tag) || tag[i] != ':' {
55+
return nil, errTagSyntax
56+
}
57+
if tag[i+1] != '"' {
58+
return nil, errTagValueSyntax
59+
}
60+
61+
key := string(tag[:i])
62+
tag = tag[i+1:]
63+
64+
// Scan quoted string to find value.
65+
i = 1
66+
for i < len(tag) && tag[i] != '"' {
67+
if tag[i] == '\\' {
68+
i++
69+
}
70+
i++
71+
}
72+
if i >= len(tag) {
73+
return nil, errTagValueSyntax
74+
}
75+
76+
qvalue := string(tag[:i+1])
77+
tag = tag[i+1:]
78+
79+
value, err := strconv.Unquote(qvalue)
80+
if err != nil {
81+
return nil, errTagValueSyntax
82+
}
83+
84+
options := strings.Split(value, ",")
85+
86+
tags[key] = options
87+
}
88+
89+
if hasTag && len(tags) == 0 {
90+
return nil, nil
91+
}
92+
93+
return tags, nil
94+
}

value_tests/WithStructTag.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package value_tests
2+
3+
//go:generate go run github.com/json-iterator/tinygo/gen
4+
type WithStructTag struct {
5+
Field1 string `json:"field1"`
6+
Field2 string `json:"-"`
7+
}

value_tests/WithStructTag_json.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package value_tests
2+
3+
import jsoniter "github.com/json-iterator/tinygo"
4+
5+
func WithStructTag_json_unmarshal(iter *jsoniter.Iterator, out *WithStructTag) {
6+
more := iter.ReadObjectHead()
7+
for more {
8+
field := iter.ReadObjectField()
9+
switch {
10+
case field == `field1`:
11+
iter.ReadString(&(*out).Field1)
12+
default:
13+
iter.Skip()
14+
}
15+
more = iter.ReadObjectMore()
16+
}
17+
}
18+
type WithStructTag_json struct {
19+
}
20+
func (json WithStructTag_json) Type() interface{} {
21+
var val WithStructTag
22+
return &val
23+
}
24+
func (json WithStructTag_json) Unmarshal(iter *jsoniter.Iterator, val interface{}) {
25+
WithStructTag_json_unmarshal(iter, val.(*WithStructTag))
26+
}

value_tests/struct_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ func Test_struct2(t *testing.T) {
1717
var val2 AnonymousStruct
1818
compareWithStdlib(`{ "Value": {"Name":"hello","Price":100} }`, jsoniter.CreateJsonAdapter(AnonymousStruct_json{}), &val1, &val2)
1919
}
20+
21+
func Test_struct3(t *testing.T) {
22+
var val1 WithStructTag
23+
var val2 WithStructTag
24+
compareWithStdlib(`{ "field1": "hello", "Field2": "world" }`, jsoniter.CreateJsonAdapter(WithStructTag_json{}), &val1, &val2)
25+
}

0 commit comments

Comments
 (0)