-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathparser.go
146 lines (123 loc) · 3.48 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package json2go
import (
"encoding/json"
"go/ast"
)
type options struct {
extractCommonTypes bool
stringPointersWhenKeyMissing bool
skipEmptyKeys bool
makeMaps bool
makeMapsWhenMinAttributes uint
timeAsStr bool
}
// JSONParserOpt is a type for setting parser options.
type JSONParserOpt func(*options)
// OptExtractCommonTypes toggles extracting common json nodes as separate types.
func OptExtractCommonTypes(v bool) JSONParserOpt {
return func(o *options) {
o.extractCommonTypes = v
}
}
// OptStringPointersWhenKeyMissing toggles wether missing string key in one of documents should result in pointer string.
func OptStringPointersWhenKeyMissing(v bool) JSONParserOpt {
return func(o *options) {
o.stringPointersWhenKeyMissing = v
}
}
// OptSkipEmptyKeys toggles skipping keys in input that were only nulls.
func OptSkipEmptyKeys(v bool) JSONParserOpt {
return func(o *options) {
o.skipEmptyKeys = v
}
}
// OptMakeMaps defines if parser should try to use maps instead of structs when possible.
// minAttributes defines minimum number of attributes in object to try converting it to a map.
func OptMakeMaps(v bool, minAttributes uint) JSONParserOpt {
return func(o *options) {
o.makeMaps = v
o.makeMapsWhenMinAttributes = minAttributes
}
}
// OptTimeAsString toggles using time.Time for valid time strings or just a string.
func OptTimeAsString(v bool) JSONParserOpt {
return func(o *options) {
o.timeAsStr = v
}
}
// JSONParser parses successive json inputs and returns go representation as string
type JSONParser struct {
rootNode *node
opts options
}
// NewJSONParser creates new json Parser
func NewJSONParser(rootTypeName string, opts ...JSONParserOpt) *JSONParser {
rootNode := newNode(rootTypeName)
rootNode.root = true
p := JSONParser{
rootNode: rootNode,
opts: options{},
}
for _, o := range opts {
o(&p.opts)
}
return &p
}
// FeedBytes consumes json input as bytes. If input is invalid, json unmarshalling error is returned
func (p *JSONParser) FeedBytes(input []byte) error {
var v interface{}
if err := json.Unmarshal(input, &v); err != nil {
return err
}
p.FeedValue(v)
return nil
}
// FeedValue consumes one of:
//
// * simple type (int, float, string, etc.)
// * []interface{} - each value must meet these requirements
// * map[string]interface{} - each value must meet these requirements
//
// json.Unmarshal to empty interface value provides perfect input (see example)
func (p *JSONParser) FeedValue(input interface{}) {
p.rootNode.grow(input)
}
// String returns string representation of go struct fitting parsed json values
func (p *JSONParser) String() string {
root := p.rootNode.clone()
root.sort()
if p.opts.skipEmptyKeys {
p.stripEmptyKeys(root)
}
if p.opts.makeMaps {
convertViableObjectsToMaps(root, p.opts.makeMapsWhenMinAttributes)
}
nodes := []*node{root}
if p.opts.extractCommonTypes {
nodes = extractCommonSubtrees(root)
}
return astPrintDecls(
astMakeDecls(nodes, p.opts),
)
}
// ASTDecls returns ast type declarations
func (p *JSONParser) ASTDecls() []ast.Decl {
p.rootNode.sort()
return astMakeDecls(
[]*node{p.rootNode},
p.opts,
)
}
func (p *JSONParser) stripEmptyKeys(n *node) {
if len(n.children) == 0 {
return
}
newChildren := make([]*node, 0, len(n.children))
for i, c := range n.children {
if c.t.id() != nodeTypeInit.id() {
p.stripEmptyKeys(c)
newChildren = append(newChildren, n.children[i])
}
}
n.children = newChildren
}