├── .travis.yml ├── README.md ├── benchmark_test.go ├── value_test.go └── value.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # json-gen 2 | 3 | [![Build Status](https://travis-ci.org/darjun/json-gen.svg?branch=master)](https://travis-ci.org/darjun/json-gen) 4 | 5 | ## 起源 6 | 7 | 游戏服务端的很多操作(包括玩家的和非玩家的)需要传给公司中台收集汇总,根据运营的需求分析数据。中台那边要求传过去的数据为 JSON 格式。开始使用 Golang 标准库中的`encoding/json`,发现性能不够理想(因为序列化使用了反射,涉及多次内存分配)。由于数据原始格式都是`map[string]interface{}`,且需要自己一个字段一个字段构造,于是我想可以在构造过程中就计算出最终 JSON 串的长度,那么就只需要一次内存分配了。 8 | 9 | ## 使用 10 | 11 | 下载: 12 | 13 | ``` 14 | $ go get github.com/darjun/json-gen 15 | ``` 16 | 17 | 引入: 18 | 19 | ``` 20 | import ( 21 | jsongen "github.com/darjun/json-gen" 22 | ) 23 | ``` 24 | 25 | 构造数组或映射: 26 | 27 | ``` 28 | // 数组 29 | a := jsongen.NewArray() 30 | a.AppendUint(123) 31 | a.AppendInt(-456) 32 | data := a.Serialize(nil) 33 | // string(data) == "[123,-456]" 34 | 35 | // 映射 36 | m := jsongen.NewMap() 37 | m.PutUint("uintkey", 123) 38 | m.PutInt("intkey", -456) 39 | data := m.Serialize(nil) 40 | // string(data) == `{"uintkey":123,"intkey":-456}` 41 | ``` 42 | 43 | 当然类型可以无限嵌套: 44 | 45 | ``` 46 | subm := jsongen.NewMap() 47 | subm.PutString("stringkey", "test string") 48 | 49 | m := jsongen.NewMap() 50 | m.PutUint("uintkey", 123) 51 | m.PutUintArray("uintarray", []uint64{123,456,789}) 52 | m.PutMap("subm", subm) 53 | data := m.Serialize(nil) 54 | // string(data) == `{"uintkey":123,"uintarray":[123,456,789],"subm":{"stringkey":"test string"}}` 55 | ``` 56 | 57 | ## Benchmark 58 | 59 | | Library | Time/op(ns) | B/op | allocs/op | 60 | |---------|---------|----------|-----------| 61 | | encoding/json | 22209 | 6673 | 127 | 62 | | darjun/json-gen | 3300 | 1152 | 1 | 63 | 64 | 通常情况下,`json-gen`生成 JSON 串的性能是标准 JSON 库的**10**。 65 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkStandardJson(b *testing.B) { 9 | b.StopTimer() 10 | m := make(map[string]interface{}) 11 | 12 | m1 := make(map[string]interface{}) 13 | m1["uintkey"] = 123 14 | m1["intkey"] = -45 15 | m1["floatkey"] = 12.34 16 | m1["boolkey"] = true 17 | m1["stringkey1"] = "teststring" 18 | m1["stringkey2"] = `string with \` 19 | m1["stringkey3"] = `string with "` 20 | m["map1"] = m1 21 | 22 | m2 := make(map[string]interface{}) 23 | m2["uintarray"] = []uint64{123, 456, 789} 24 | m2["intarray"] = []int64{-23, -45, -89} 25 | m2["floatarray"] = []float64{12.34, -56.78, 90} 26 | m2["boolarray"] = []bool{true, false, true} 27 | m2["stringarray"] = []string{"test string", `string with \`, `string with "`} 28 | m["map2"] = m2 29 | 30 | m3 := make(map[string]interface{}) 31 | { 32 | a1 := []interface{}{123, -45, 12.34, true, "test string", `string with \`, `string with "`} 33 | a2 := []interface{}{[]uint64{123, 456, 789}, []int64{-12, -45, -78}, []float64{12.34, -56.78, 9.0}, []bool{true, false, true}} 34 | a3 := []interface{}{ 35 | map[string]interface{}{ 36 | "uintkey": 123, 37 | "intkey": -456, 38 | "floatkey": 12.34, 39 | "boolkey": true, 40 | "stringkey": "test string", 41 | }, 42 | map[string]interface{}{ 43 | "uintkey": 455, 44 | "intkey": -789, 45 | "floatkey": 56.78, 46 | "boolkey": false, 47 | "stringkey": `string with \`, 48 | }, 49 | } 50 | m3["array1"] = a1 51 | m3["array2"] = a2 52 | m3["array3"] = a3 53 | } 54 | { 55 | a1 := []interface{}{123, -45, 12.34, true, "test string", `string with \`, `string with "`} 56 | a2 := []interface{}{[]uint64{123, 456, 789}, []int64{-12, -45, -78}, []float64{12.34, -56.78, 9.0}, []bool{true, false, true}} 57 | a3 := []interface{}{ 58 | map[string]interface{}{ 59 | "uintkey": 123, 60 | "intkey": -456, 61 | "floatkey": 12.34, 62 | "boolkey": true, 63 | "stringkey": "test string", 64 | }, 65 | map[string]interface{}{ 66 | "uintkey": 455, 67 | "intkey": -789, 68 | "floatkey": 56.78, 69 | "boolkey": false, 70 | "stringkey": `string with \`, 71 | }, 72 | } 73 | m3["array4"] = []interface{}{a1, a2, a3} 74 | } 75 | m["map3"] = m3 76 | 77 | b.StartTimer() 78 | 79 | for i := 0; i < b.N; i++ { 80 | json.Marshal(m) 81 | } 82 | } 83 | 84 | func BenchmarkGen(b *testing.B) { 85 | b.StopTimer() 86 | m, _ := map4() 87 | b.StartTimer() 88 | 89 | for i := 0; i < b.N; i++ { 90 | m.Serialize(nil) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /value_test.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestUnquotedValue(t *testing.T) { 10 | testCases := []struct { 11 | value UnquotedValue 12 | expected string 13 | }{ 14 | {"1234", "1234"}, 15 | {"12.34", "12.34"}, 16 | } 17 | 18 | for _, c := range testCases { 19 | if string(c.value.Serialize(nil)) != c.expected { 20 | t.Errorf("actual(%s) != expected(%s)", string(c.value.Serialize(nil)), c.expected) 21 | } 22 | } 23 | } 24 | 25 | func TestQuotedValue(t *testing.T) { 26 | testCases := []struct { 27 | value QuotedValue 28 | expected string 29 | }{ 30 | {QuotedValue("string"), `"string"`}, 31 | {QuotedValue(`string with \`), `"string with \"`}, 32 | {QuotedValue(`string with "`), `"string with ""`}, 33 | } 34 | 35 | for _, c := range testCases { 36 | if string(c.value.Serialize(nil)) != c.expected { 37 | t.Errorf("actual(%s) != expected(%s)", string(c.value.Serialize(nil)), c.expected) 38 | } 39 | } 40 | } 41 | 42 | func array1() (*Array, string) { 43 | a1 := NewArray() 44 | a1.AppendUint(123) 45 | a1.AppendInt(-45) 46 | a1.AppendFloat(12.34) 47 | a1.AppendBool(true) 48 | a1.AppendString("test string") 49 | a1.AppendString(`string with \`) 50 | a1.AppendString(`string with "`) 51 | expected1 := `[123,-45,12.34,true,"test string","string with \\","string with \""]` 52 | 53 | return a1, expected1 54 | } 55 | 56 | func array2() (*Array, string) { 57 | a2 := NewArray() 58 | a2.AppendUintArray([]uint64{123, 456, 789}) 59 | a2.AppendIntArray([]int64{-12, -45, -78}) 60 | a2.AppendFloatArray([]float64{12.34, -56.78, 9.0}) 61 | a2.AppendBoolArray([]bool{true, false, true}) 62 | a2.AppendStringArray([]string{"test string", `string with \`, `string with "`}) 63 | expected2 := `[[123,456,789],[-12,-45,-78],[12.34,-56.78,9],[true,false,true],["test string","string with \\","string with \""]]` 64 | 65 | return a2, expected2 66 | } 67 | 68 | func array3() (*Array, string) { 69 | a3 := NewArray() 70 | m1 := NewMap() 71 | m1.PutUint("uintkey", 123) 72 | m1.PutInt("intkey", -456) 73 | m1.PutFloat("floatkey", 12.34) 74 | m1.PutBool("boolkey", true) 75 | m1.PutString("stringkey", "test string") 76 | a3.AppendMap(m1) 77 | 78 | m2 := NewMap() 79 | m2.PutUint("uintkey", 456) 80 | m2.PutInt("intkey", -789) 81 | m2.PutFloat("floatkey", 56.78) 82 | m2.PutBool("boolkey", false) 83 | m2.PutString("stringkey", `string with \`) 84 | a3.AppendMap(m2) 85 | expected3 := `[{"uintkey":123,"intkey":-456,"floatkey":12.34,"boolkey":true,"stringkey":"test string"},{"uintkey":456,"intkey":-789,"floatkey":56.78,"boolkey":false,"stringkey":"string with \\"}]` 86 | return a3, expected3 87 | } 88 | 89 | func array4() (*Array, string) { 90 | a4 := NewArray() 91 | 92 | a1, expected1 := array1() 93 | a2, expected2 := array2() 94 | a3, expected3 := array3() 95 | a4.AppendArray(*a1) 96 | a4.AppendArray(*a2) 97 | a4.AppendArray(*a3) 98 | expected4 := fmt.Sprintf("[%s,%s,%s]", expected1, expected2, expected3) 99 | 100 | return a4, expected4 101 | } 102 | 103 | func TestArrayValue(t *testing.T) { 104 | a1, expected1 := array1() 105 | 106 | a2, expected2 := array2() 107 | 108 | a3, expected3 := array3() 109 | 110 | a4, expected4 := array4() 111 | 112 | testCases := []struct { 113 | name string 114 | value *Array 115 | expected string 116 | }{ 117 | {"basic", a1, expected1}, 118 | {"primitive array", a2, expected2}, 119 | {"map array", a3, expected3}, 120 | {"nested general array", a4, expected4}, 121 | } 122 | 123 | for _, c := range testCases { 124 | data := c.value.Serialize(nil) 125 | if string(data) != c.expected { 126 | t.Errorf("array name:%s actual:%s != expected:%s", c.name, string(data), c.expected) 127 | } 128 | 129 | if len(data) != c.value.Size() { 130 | t.Errorf("array name:%s buf size error, actual:%d, expected:%d", c.name, len(data), c.value.Size()) 131 | } 132 | 133 | var obj []interface{} 134 | if err := json.Unmarshal(data, &obj); err != nil { 135 | t.Errorf("array name:%s unmarshal error:%v", c.name, err) 136 | } else { 137 | t.Logf("array name:%s unmarshal: %v", c.name, obj) 138 | } 139 | } 140 | } 141 | 142 | func map1() (*Map, string) { 143 | m1 := NewMap() 144 | m1.PutUint("uintkey", 123) 145 | m1.PutInt("intkey", -45) 146 | m1.PutFloat("floatkey", 12.34) 147 | m1.PutBool("boolkey", true) 148 | m1.PutString("stringkey1", "teststring") 149 | m1.PutString("stringkey2", `string with \`) 150 | m1.PutString("stringkey3", `string with "`) 151 | expected1 := `{"uintkey":123,"intkey":-45,"floatkey":12.34,"boolkey":true,"stringkey1":"teststring","stringkey2":"string with \\","stringkey3":"string with \""}` 152 | 153 | return m1, expected1 154 | } 155 | 156 | func map2() (*Map, string) { 157 | m2 := NewMap() 158 | m2.PutUintArray("uintarray", []uint64{123, 456, 789}) 159 | m2.PutIntArray("intarray", []int64{-23, -45, -89}) 160 | m2.PutFloatArray("floatarray", []float64{12.34, -56.78, 90}) 161 | m2.PutBoolArray("boolarray", []bool{true, false, true}) 162 | m2.PutStringArray("stringarray", []string{"test string", `string with \`, `string with "`}) 163 | expected2 := `{"uintarray":[123,456,789],"intarray":[-23,-45,-89],"floatarray":[12.34,-56.78,90],"boolarray":[true,false,true],"stringarray":["test string","string with \\","string with \""]}` 164 | 165 | return m2, expected2 166 | } 167 | 168 | func map3() (*Map, string) { 169 | m3 := NewMap() 170 | 171 | a1, expected1 := array1() 172 | a2, expected2 := array2() 173 | a3, expected3 := array3() 174 | a4, expected4 := array4() 175 | 176 | m3.PutArray("array1", a1) 177 | m3.PutArray("array2", a2) 178 | m3.PutArray("array3", a3) 179 | m3.PutArray("array4", a4) 180 | 181 | expected := fmt.Sprintf(`{"array1":%s,"array2":%s,"array3":%s,"array4":%s}`, expected1, expected2, expected3, expected4) 182 | 183 | return m3, expected 184 | } 185 | 186 | func map4() (*Map, string) { 187 | m4 := NewMap() 188 | 189 | m1, expected1 := map1() 190 | m2, expected2 := map2() 191 | m3, expected3 := map3() 192 | 193 | m4.PutMap("map1", m1) 194 | m4.PutMap("map2", m2) 195 | m4.PutMap("map3", m3) 196 | 197 | expected := fmt.Sprintf(`{"map1":%s,"map2":%s,"map3":%s}`, expected1, expected2, expected3) 198 | 199 | return m4, expected 200 | } 201 | 202 | func TestMapValue(t *testing.T) { 203 | m1, expected1 := map1() 204 | m2, expected2 := map2() 205 | m3, expected3 := map3() 206 | m4, expected4 := map4() 207 | 208 | testCases := []struct { 209 | name string 210 | value *Map 211 | expected string 212 | }{ 213 | {"basic", m1, expected1}, 214 | {"primitive array", m2, expected2}, 215 | {"general array", m3, expected3}, 216 | {"nested map", m4, expected4}, 217 | } 218 | 219 | for _, c := range testCases { 220 | data := c.value.Serialize(nil) 221 | if string(data) != c.expected { 222 | t.Errorf("map name:%s actual:%s != expected:%s", c.name, string(data), c.expected) 223 | } 224 | 225 | if len(data) != c.value.Size() { 226 | t.Errorf("map name:%s buf size error, actual:%d, expected:%d", c.name, len(data), c.value.Size()) 227 | } 228 | 229 | var obj map[string]interface{} 230 | if err := json.Unmarshal(data, &obj); err != nil { 231 | t.Errorf("map name:%s unmarshal error:%v", c.name, err) 232 | } else { 233 | t.Logf("map name:%s unmarshal: %v", c.name, obj) 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | ) 7 | 8 | // Value 表示将要序列化到`json`字符串中的值 9 | type Value interface { 10 | // Serialize 将值序列化为字符串,追加到`buf`后,返回新的`buf` 11 | Serialize(buf []byte) []byte 12 | // Size 返回值在最终的`json`串中占有多少字节 13 | Size() int 14 | } 15 | 16 | // QuotedValue 表示需要用"包裹起来的值,例如字符串 17 | type QuotedValue string 18 | 19 | // Serialize 将`q`序列化为字符串,追加到`buf`后,返回新的`buf` 20 | func (q QuotedValue) Serialize(buf []byte) []byte { 21 | buf = append(buf, '"') 22 | buf = append(buf, []byte(q)...) 23 | return append(buf, '"') 24 | } 25 | 26 | // Size 返回`q`在最终的`json`串中占有多少字节 27 | func (q QuotedValue) Size() int { 28 | return len(q) + 2 29 | } 30 | 31 | // UnquotedValue 表示不需要用"包裹起来的值,例如整数,浮点数等 32 | type UnquotedValue string 33 | 34 | // Serialize 将`u`序列化为字符串,追加到`buf`后,返回新的`buf` 35 | func (u UnquotedValue) Serialize(buf []byte) []byte { 36 | return append(buf, []byte(u)...) 37 | } 38 | 39 | // Size 返回`u`在最终的`json`串中占有多少字节 40 | func (u UnquotedValue) Size() int { 41 | return len(u) 42 | } 43 | 44 | // Array 表示一个`json`数组 45 | type Array []Value 46 | 47 | // NewArray 创建一个`json`数组,返回其指针 48 | func NewArray() *Array { 49 | a := Array(make([]Value, 0, 1)) 50 | return &a 51 | } 52 | 53 | // Serialize 将`a`序列化为字符串,追加到`buf`后,返回新的`buf` 54 | func (a Array) Serialize(buf []byte) []byte { 55 | if len(buf) == 0 { 56 | buf = make([]byte, 0, a.Size()) 57 | } 58 | 59 | buf = append(buf, '[') 60 | count := len(a) 61 | for i, e := range a { 62 | buf = e.Serialize(buf) 63 | if i != count-1 { 64 | buf = append(buf, ',') 65 | } 66 | } 67 | 68 | return append(buf, ']') 69 | } 70 | 71 | // Size 返回`a`在最终的`json`串中占有多少字节 72 | func (a Array) Size() int { 73 | size := 0 74 | for _, e := range a { 75 | size += e.Size() 76 | } 77 | 78 | // for [] 79 | size += 2 80 | 81 | if len(a) > 1 { 82 | // for , 83 | size += len(a) - 1 84 | } 85 | 86 | return size 87 | } 88 | 89 | // AppendUint 将`uint64`类型的值`u`追加到数组`a`后 90 | func (a *Array) AppendUint(u uint64) { 91 | value := strconv.FormatUint(u, 10) 92 | 93 | *a = append(*a, UnquotedValue(value)) 94 | } 95 | 96 | // AppendInt 将`int64`类型的值`i`追加到数组`a`后 97 | func (a *Array) AppendInt(i int64) { 98 | value := strconv.FormatInt(i, 10) 99 | 100 | *a = append(*a, UnquotedValue(value)) 101 | } 102 | 103 | // AppendFloat 将`float64`类型的值`f`追加到数组`a`后 104 | func (a *Array) AppendFloat(f float64) { 105 | value := strconv.FormatFloat(f, 'g', 10, 64) 106 | 107 | *a = append(*a, UnquotedValue(value)) 108 | } 109 | 110 | // AppendBool 将`bool`类型的值`b`追加到数组`a`后 111 | func (a *Array) AppendBool(b bool) { 112 | value := strconv.FormatBool(b) 113 | 114 | *a = append(*a, UnquotedValue(value)) 115 | } 116 | 117 | // AppendString 将`string`类型的值`s`追加到数组`a`后 118 | func (a *Array) AppendString(value string) { 119 | *a = append(*a, QuotedValue(escapeString(value))) 120 | } 121 | 122 | // AppendMap 将`Map`类型的值`m`追加到数组`a`后 123 | func (a *Array) AppendMap(m *Map) { 124 | *a = append(*a, m) 125 | } 126 | 127 | // AppendUintArray 将`uint64`数组`u`追加到数组`a`后 128 | func (a *Array) AppendUintArray(u []uint64) { 129 | value := make([]Value, 0, len(u)) 130 | for _, v := range u { 131 | value = append(value, UnquotedValue(strconv.FormatUint(v, 10))) 132 | } 133 | 134 | *a = append(*a, Array(value)) 135 | } 136 | 137 | // AppendIntArray 将`int64`数组`i`追加到数组`a`后 138 | func (a *Array) AppendIntArray(i []int64) { 139 | value := make([]Value, 0, len(i)) 140 | for _, v := range i { 141 | value = append(value, UnquotedValue(strconv.FormatInt(v, 10))) 142 | } 143 | 144 | *a = append(*a, Array(value)) 145 | } 146 | 147 | // AppendFloatArray 将`float64`数组`f`追加到数组`a`后 148 | func (a *Array) AppendFloatArray(f []float64) { 149 | value := make([]Value, 0, len(f)) 150 | for _, v := range f { 151 | value = append(value, UnquotedValue(strconv.FormatFloat(v, 'g', 10, 64))) 152 | } 153 | 154 | *a = append(*a, Array(value)) 155 | } 156 | 157 | // AppendBoolArray 将`bool`数组`b`追加到数组`a`后 158 | func (a *Array) AppendBoolArray(b []bool) { 159 | value := make([]Value, 0, len(b)) 160 | for _, v := range b { 161 | value = append(value, UnquotedValue(strconv.FormatBool(v))) 162 | } 163 | 164 | *a = append(*a, Array(value)) 165 | } 166 | 167 | // AppendStringArray 将`string`数组`s`追加到数组`a`后 168 | func (a *Array) AppendStringArray(s []string) { 169 | value := make([]Value, 0, len(s)) 170 | for _, v := range s { 171 | value = append(value, QuotedValue(escapeString(v))) 172 | } 173 | 174 | *a = append(*a, Array(value)) 175 | } 176 | 177 | // AppendMapArray 将`Map`数组`m`追加到数组`a`后 178 | func (a *Array) AppendMapArray(m []Map) { 179 | value := make([]Value, 0, len(m)) 180 | for _, v := range m { 181 | value = append(value, v) 182 | } 183 | 184 | *a = append(*a, Array(value)) 185 | } 186 | 187 | // AppendArray 将`json`数组`oa`追加到数组`a`后 188 | func (a *Array) AppendArray(oa Array) { 189 | *a = append(*a, oa) 190 | } 191 | 192 | // Map 表示一个`json`映射 193 | type Map struct { 194 | keys []string 195 | values []Value 196 | } 197 | 198 | // Serialize 将`m`序列化为字符串,追加到`buf`后,返回新的`buf` 199 | func (m Map) Serialize(buf []byte) []byte { 200 | if len(buf) == 0 { 201 | buf = make([]byte, 0, m.Size()) 202 | } 203 | 204 | buf = append(buf, '{') 205 | count := len(m.keys) 206 | for i, key := range m.keys { 207 | buf = append(buf, '"') 208 | buf = append(buf, []byte(key)...) 209 | buf = append(buf, '"') 210 | buf = append(buf, ':') 211 | buf = m.values[i].Serialize(buf) 212 | if i != count-1 { 213 | buf = append(buf, ',') 214 | } 215 | } 216 | return append(buf, '}') 217 | } 218 | 219 | // Size 返回`m`在最终的`json`串中占有多少字节 220 | func (m Map) Size() int { 221 | size := 0 222 | for i, key := range m.keys { 223 | // +2 for ", +1 for : 224 | size += len(key) + 2 + 1 225 | size += m.values[i].Size() 226 | } 227 | 228 | // +2 for {} 229 | size += 2 230 | 231 | if len(m.keys) > 1 { 232 | // for , 233 | size += len(m.keys) - 1 234 | } 235 | 236 | return size 237 | } 238 | 239 | func (m *Map) put(key string, value Value) { 240 | m.keys = append(m.keys, key) 241 | m.values = append(m.values, value) 242 | } 243 | 244 | // PutUint 将`uint64`类型的值`u`与键`key`关联 245 | func (m *Map) PutUint(key string, u uint64) { 246 | value := strconv.FormatUint(u, 10) 247 | 248 | m.put(key, UnquotedValue(value)) 249 | } 250 | 251 | // PutInt 将`int64`类型的值`i`与键`key`关联 252 | func (m *Map) PutInt(key string, i int64) { 253 | value := strconv.FormatInt(i, 10) 254 | 255 | m.put(key, UnquotedValue(value)) 256 | } 257 | 258 | // PutFloat 将`float64`类型的值`f`与键`key`关联 259 | func (m *Map) PutFloat(key string, f float64) { 260 | value := strconv.FormatFloat(f, 'g', 10, 64) 261 | 262 | m.put(key, UnquotedValue(value)) 263 | } 264 | 265 | // PutBool 将`bool`类型的值`b`与键`key`关联 266 | func (m *Map) PutBool(key string, b bool) { 267 | value := strconv.FormatBool(b) 268 | 269 | m.put(key, UnquotedValue(value)) 270 | } 271 | 272 | // PutString 将`string`类型的值`value`与键`key`关联 273 | func (m *Map) PutString(key, value string) { 274 | m.put(key, QuotedValue(escapeString(value))) 275 | } 276 | 277 | // PutUintArray 将`uint64`数组类型的值`u`与键`key`关联 278 | func (m *Map) PutUintArray(key string, u []uint64) { 279 | value := make([]Value, 0, len(u)) 280 | for _, v := range u { 281 | value = append(value, UnquotedValue(strconv.FormatUint(v, 10))) 282 | } 283 | 284 | m.put(key, Array(value)) 285 | } 286 | 287 | // PutIntArray 将`int64`数组类型的值`i`与键`key`关联 288 | func (m *Map) PutIntArray(key string, i []int64) { 289 | value := make([]Value, 0, len(i)) 290 | for _, v := range i { 291 | value = append(value, UnquotedValue(strconv.FormatInt(v, 10))) 292 | } 293 | 294 | m.put(key, Array(value)) 295 | } 296 | 297 | // PutFloatArray 将`float64`数组类型的值`f`与键`key`关联 298 | func (m *Map) PutFloatArray(key string, f []float64) { 299 | value := make([]Value, 0, len(f)) 300 | for _, v := range f { 301 | value = append(value, UnquotedValue(strconv.FormatFloat(v, 'g', 10, 64))) 302 | } 303 | 304 | m.put(key, Array(value)) 305 | } 306 | 307 | // PutBoolArray 将`bool`数组类型的值`b`与键`key`关联 308 | func (m *Map) PutBoolArray(key string, b []bool) { 309 | value := make([]Value, 0, len(b)) 310 | for _, v := range b { 311 | value = append(value, UnquotedValue(strconv.FormatBool(v))) 312 | } 313 | 314 | m.put(key, Array(value)) 315 | } 316 | 317 | // PutStringArray 将`string`数组类型的值`s`与键`key`关联 318 | func (m *Map) PutStringArray(key string, s []string) { 319 | value := make([]Value, 0, len(s)) 320 | for _, v := range s { 321 | value = append(value, QuotedValue(escapeString(v))) 322 | } 323 | 324 | m.put(key, Array(value)) 325 | } 326 | 327 | // PutArray 将`json`数组`a`与键`key`关联 328 | func (m *Map) PutArray(key string, a *Array) { 329 | m.put(key, a) 330 | } 331 | 332 | // PutMap 将`json`映射`om`与键`key`关联 333 | func (m *Map) PutMap(key string, om *Map) { 334 | m.put(key, om) 335 | } 336 | 337 | // NewMap 创建一个`json`映射返回其指针 338 | func NewMap() *Map { 339 | return &Map{ 340 | keys: make([]string, 0, 8), 341 | values: make([]Value, 0, 8), 342 | } 343 | } 344 | 345 | func escapeString(s string) string { 346 | var buf bytes.Buffer 347 | for _, r := range s { 348 | if r == '\\' || r == '"' { 349 | buf.WriteByte('\\') 350 | } 351 | buf.WriteRune(r) 352 | } 353 | 354 | return buf.String() 355 | } 356 | --------------------------------------------------------------------------------