github.com/dubbogo/gost@v1.14.0/encoding/json/parser.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package json 19 20 import ( 21 "io/ioutil" 22 "log" 23 "reflect" 24 "strconv" 25 "strings" 26 "time" 27 ) 28 29 import ( 30 "github.com/buger/jsonparser" 31 32 perrors "github.com/pkg/errors" 33 ) 34 35 // HessianRegisterPair define the pair to register to hessian 36 type HessianRegisterPair struct { 37 JavaClassName string 38 Obj interface{} 39 } 40 41 // jsonStructParser can use reflect to create arbitrary interface{} of go, from user defined json file. 42 type jsonStructParser struct { 43 structFields []reflect.StructField 44 hessianRegisterPair []HessianRegisterPair 45 valueMap map[string]string 46 subObjValueMap map[string]reflect.Value 47 } 48 49 // newJSONStructParser create a new json struct parser 50 func newJSONStructParser() *jsonStructParser { 51 return &jsonStructParser{ 52 structFields: make([]reflect.StructField, 0, 16), 53 valueMap: make(map[string]string, 8), 54 hessianRegisterPair: make([]HessianRegisterPair, 0, 16), 55 subObjValueMap: make(map[string]reflect.Value, 8), 56 } 57 } 58 59 // File2Interface first read json byte from @filePath, and parse it to interface 60 func File2Interface(filePath string) ([]HessianRegisterPair, interface{}, error) { 61 defer func() { 62 defaultJSONStructParser = newJSONStructParser() 63 }() 64 return defaultJSONStructParser.jsonFilePath2Struct(filePath) 65 } 66 67 func init() { 68 defaultJSONStructParser = newJSONStructParser() 69 } 70 71 var defaultJSONStructParser *jsonStructParser 72 73 // RemoveTargetNameField remove target file in @v 74 func RemoveTargetNameField(v interface{}, targetName string) interface{} { 75 defer func() { 76 defaultJSONStructParser = newJSONStructParser() 77 }() 78 return defaultJSONStructParser.removeTargetNameField(v, targetName) 79 } 80 81 func (jsp *jsonStructParser) cb(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { 82 switch dataType { 83 case jsonparser.Object: 84 // parse sub interface, use a new parser to deal with it the same way 85 newParser := newJSONStructParser() 86 subObj := newParser.json2Struct(value) 87 javaClassName, err := getJavaClassName(subObj) 88 if err != nil { 89 return err 90 } 91 jsp.hessianRegisterPair = append(jsp.hessianRegisterPair, HessianRegisterPair{ 92 JavaClassName: javaClassName, 93 Obj: subObj, 94 }) 95 jsp.structFields = append(jsp.structFields, reflect.StructField{ 96 Name: string(key), 97 Type: reflect.TypeOf(subObj), 98 }) 99 jsp.subObjValueMap[string(key)] = reflect.ValueOf(subObj) 100 101 case jsonparser.Array: // TODO slice parse 102 case jsonparser.String: // normal struct parse 103 // "type@value" 104 arr := strings.Split(string(value), "@") 105 var userDefinedType reflect.Type 106 switch arr[0] { 107 case "int": 108 userDefinedType = reflect.TypeOf(0) 109 case "string": 110 userDefinedType = reflect.TypeOf("") 111 case "uint64": 112 userDefinedType = reflect.TypeOf(uint64(0)) 113 case "time.Time": 114 userDefinedType = reflect.TypeOf(time.Time{}) 115 case "float32": 116 userDefinedType = reflect.TypeOf(float32(0)) 117 case "float64": 118 userDefinedType = reflect.TypeOf(float64(0)) 119 case "bool": 120 userDefinedType = reflect.TypeOf(false) 121 default: 122 log.Printf("error: dataType %s in json is not supported\n", string(value)) 123 return perrors.Errorf("dataType %s in json is not supported", string(value)) 124 } 125 if len(arr) > 1 { 126 jsp.valueMap[string(key)] = arr[1] 127 } 128 jsp.structFields = append(jsp.structFields, reflect.StructField{ 129 Name: string(key), 130 Type: userDefinedType, 131 }) 132 default: 133 log.Printf("error: dataType %s in json is not supported\n", string(value)) 134 return perrors.Errorf("dataType %s in json is not supported", string(value)) 135 } 136 return nil 137 } 138 139 // json2Struct parse data from json file to user defined interface 140 func (jsp *jsonStructParser) json2Struct(jsonData []byte) interface{} { 141 // first: call ObjectEach to parse jsonData to reflect.StructField item 142 if err := jsonparser.ObjectEach(jsonData, jsp.cb); err != nil { 143 log.Println("jsonparser.ObjectEach error = ", err) 144 } 145 146 // second: parse structField to reflectType 147 typ := reflect.StructOf(jsp.structFields) 148 v := reflect.New(typ).Elem() 149 newty := reflect.TypeOf(v.Addr().Interface()).Elem() 150 151 // finally: traverse each json field, and set user defined value 152 for i := 0; i < typ.NumField(); i++ { 153 valStr, ok1 := jsp.valueMap[newty.Field(i).Name] 154 subObj, ok2 := jsp.subObjValueMap[newty.Field(i).Name] 155 if !ok1 && !ok2 { 156 continue 157 } 158 159 if newty.Field(i).Type.Kind() == reflect.Ptr { 160 v.Field(i).Set(subObj) 161 continue 162 } 163 switch newty.Field(i).Type { 164 case reflect.TypeOf(0), reflect.TypeOf(uint64(0)): 165 if parsedInt, err := strconv.Atoi(valStr); err == nil { 166 v.Field(i).SetInt(int64(parsedInt)) 167 break 168 } 169 v.Field(i).SetInt(0) 170 case reflect.TypeOf(""): 171 v.Field(i).SetString(valStr) 172 case reflect.TypeOf(time.Time{}): 173 // todo time support v.Field(i). 174 case reflect.TypeOf(float64(0)), reflect.TypeOf(float32(0)): 175 if parsedFloat, err := strconv.ParseFloat(valStr, 64); err == nil { 176 v.Field(i).SetFloat(parsedFloat) 177 break 178 } 179 v.Field(i).SetFloat(0) 180 case reflect.TypeOf(false): 181 if valStr == "true" || valStr == "1" { 182 v.Field(i).SetBool(true) 183 } 184 default: 185 log.Printf("error: val %s in value is not supported\n", valStr) 186 return perrors.Errorf("val %s in value is not supported", valStr) 187 } 188 } 189 s := v.Addr().Interface() 190 return s 191 } 192 193 // jsonFilePath2Struct read file from @filePath and parse data to interface 194 func (jsp *jsonStructParser) jsonFilePath2Struct(filePath string) ([]HessianRegisterPair, interface{}, error) { 195 jsonData, err := ioutil.ReadFile(filePath) 196 if err != nil { 197 return []HessianRegisterPair{}, nil, err 198 } 199 return jsp.hessianRegisterPair, jsp.json2Struct(jsonData), nil 200 } 201 202 // removeTargetNameField remove origin interface @v's target field by @targetName 203 func (jsp *jsonStructParser) removeTargetNameField(v interface{}, targetName string) interface{} { 204 typ := reflect.TypeOf(v).Elem() 205 val := reflect.ValueOf(v).Elem() 206 nums := val.NumField() 207 structFields := make([]reflect.StructField, 0) 208 fieldMap := make(map[string]reflect.Value) 209 for i := 0; i < nums; i++ { 210 if typ.Field(i).Name != targetName { 211 structFields = append(structFields, reflect.StructField{ 212 Name: typ.Field(i).Name, 213 Type: typ.Field(i).Type, 214 }) 215 fieldMap[typ.Field(i).Name] = val.Field(i) 216 } 217 } 218 newtyp := reflect.StructOf(structFields) 219 newi := reflect.New(newtyp).Elem() 220 newty := reflect.TypeOf(newi.Addr().Interface()).Elem() 221 for i := 0; i < nums-1; i++ { 222 newi.Field(i).Set(fieldMap[newty.Field(i).Name]) 223 } 224 return newi.Addr().Interface() 225 } 226 227 // getJavaClassName can read field JavaClassName of interface{}, used in cli-tool to do hessian registry 228 func getJavaClassName(pkg interface{}) (string, error) { 229 val := reflect.ValueOf(pkg).Elem() 230 typ := reflect.TypeOf(pkg).Elem() 231 nums := val.NumField() 232 for i := 0; i < nums; i++ { 233 if typ.Field(i).Name == "JavaClassName" { 234 return val.Field(i).String(), nil 235 } 236 } 237 return "", perrors.Errorf("JavaClassName field not found error") 238 }