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  }