Commit f24f051f authored by 李志信's avatar 李志信

ftr: add json struct parser to support cli tool

parent 522be9af
module github.com/dubbogo/gost
require (
github.com/apache/dubbo-go-hessian2 v1.8.0-rc1
github.com/buger/jsonparser v1.0.0
github.com/davecgh/go-spew v1.1.1
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
github.com/k0kubun/pp v3.0.1+incompatible
......
github.com/apache/dubbo-go-hessian2 v1.8.0-rc1 h1:IJyHFLhJrNE5VdKtN6/0n7rJqCjHraHuORcHkZhwQyQ=
github.com/apache/dubbo-go-hessian2 v1.8.0-rc1/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
github.com/buger/jsonparser v1.0.0 h1:etJTGF5ESxjI0Ic2UaLQs2LQQpa8G9ykQScukbh4L8A=
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
......@@ -10,12 +15,14 @@ github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
......@@ -24,5 +31,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepx
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package json_struct_parser
import (
"fmt"
"io/ioutil"
"log"
"reflect"
"strconv"
"strings"
"time"
hessian2 "github.com/apache/dubbo-go-hessian2"
"github.com/buger/jsonparser"
)
type jsonStructParser struct {
structFields []reflect.StructField
valueMap map[string]string
subObjValueMap map[string]reflect.Value
}
func newJsonStructParser() *jsonStructParser {
return &jsonStructParser{
structFields: make([]reflect.StructField, 0),
valueMap: make(map[string]string),
subObjValueMap: make(map[string]reflect.Value),
}
}
func init() {
defaultJsonStructParser = newJsonStructParser()
}
var defaultJsonStructParser *jsonStructParser
func JsonFile2Interface(path string) (interface{}, error) {
defer func() {
defaultJsonStructParser = newJsonStructParser()
}()
return defaultJsonStructParser.Jsonfile2Struct(path)
}
func RemoveTargetNameField(v interface{}, targetName string) interface{} {
defer func() {
defaultJsonStructParser = newJsonStructParser()
}()
return defaultJsonStructParser.RemoveTargetNameField(v, targetName)
}
func (jsp *jsonStructParser) cb(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
switch dataType {
case jsonparser.Object: // 嵌套子结构
newParser := newJsonStructParser()
subObj := newParser.json2Struct(value)
hessian2.RegisterPOJOMapping(getJavaClassName(subObj), subObj)
jsp.structFields = append(jsp.structFields, reflect.StructField{
Name: string(key),
Type: reflect.TypeOf(subObj),
})
jsp.subObjValueMap[string(key)] = reflect.ValueOf(subObj)
case jsonparser.Array: //数组结构
newParser := newJsonStructParser()
subObj := newParser.json2Struct(value)
hessian2.RegisterPOJOMapping(getJavaClassName(subObj), subObj) // TODO 目前存在问题
jsp.structFields = append(jsp.structFields, reflect.StructField{
Name: string(key),
Type: reflect.TypeOf(subObj),
})
case jsonparser.String: // 正常结构
// "type@value"
arr := strings.Split(string(value), "@")
var userDefinedType reflect.Type
switch arr[0] {
case "int":
userDefinedType = reflect.TypeOf(0)
case "string":
userDefinedType = reflect.TypeOf("")
case "uint64":
userDefinedType = reflect.TypeOf(uint64(0))
case "time.Time":
userDefinedType = reflect.TypeOf(time.Time{})
case "float32":
userDefinedType = reflect.TypeOf(float32(0))
case "float64":
userDefinedType = reflect.TypeOf(float64(0))
case "bool":
userDefinedType = reflect.TypeOf(false)
default:
log.Println("warning: val", string(value), " in json is not supported")
}
if len(arr) > 1 {
jsp.valueMap[string(key)] = arr[1]
}
jsp.structFields = append(jsp.structFields, reflect.StructField{
Name: string(key),
Type: userDefinedType,
})
default:
log.Println("warning: dataType ", string(value), " in json is not supported")
}
return nil
}
func (jsp *jsonStructParser) json2Struct(jsonData []byte) interface{} {
if err := jsonparser.ObjectEach(jsonData, jsp.cb); err != nil {
log.Println("jsonparser.ObjectEach error = ", err)
}
typ := reflect.StructOf(jsp.structFields)
v := reflect.New(typ).Elem()
newty := reflect.TypeOf(v.Addr().Interface()).Elem()
for i := 0; i < typ.NumField(); i++ {
valStr, ok1 := jsp.valueMap[newty.Field(i).Name]
subObj, ok2 := jsp.subObjValueMap[newty.Field(i).Name]
if !ok1 && !ok2 {
continue
}
if newty.Field(i).Type.Kind() == reflect.Ptr {
v.Field(i).Set(subObj)
continue
}
switch newty.Field(i).Type {
case reflect.TypeOf(0), reflect.TypeOf(uint64(0)):
if parsedInt, err := strconv.Atoi(valStr); err == nil {
v.Field(i).SetInt(int64(parsedInt))
break
}
v.Field(i).SetInt(0)
case reflect.TypeOf(""):
v.Field(i).SetString(valStr)
case reflect.TypeOf(time.Time{}):
//todo time support v.Field(i).
case reflect.TypeOf(float64(0)), reflect.TypeOf(float32(0)):
if parsedFloat, err := strconv.ParseFloat(valStr, 64); err == nil {
v.Field(i).SetFloat(parsedFloat)
break
}
v.Field(i).SetFloat(0)
case reflect.TypeOf(false):
if valStr == "true" || valStr == "1" {
v.Field(i).SetBool(true)
}
default:
log.Println("warning val: ", valStr, " in value is not supported")
}
}
s := v.Addr().Interface()
return s
}
// Jsonfile2Struct read file from @filePath and parse data to interface
func (jsp *jsonStructParser) Jsonfile2Struct(filePath string) (interface{}, error) {
jsonData, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
return jsp.json2Struct(jsonData), nil
}
// RemoveTargetNameField remove origin interface @v's target field by @targetName
func (jsp *jsonStructParser) RemoveTargetNameField(v interface{}, targetName string) interface{} {
typ := reflect.TypeOf(v).Elem()
val := reflect.ValueOf(v).Elem()
nums := val.NumField()
structFields := make([]reflect.StructField, 0)
fieldMap := make(map[string]reflect.Value)
for i := 0; i < nums; i++ {
if typ.Field(i).Name != targetName {
structFields = append(structFields, reflect.StructField{
Name: typ.Field(i).Name,
Type: typ.Field(i).Type,
})
fieldMap[typ.Field(i).Name] = val.Field(i)
}
}
newtyp := reflect.StructOf(structFields)
newi := reflect.New(newtyp).Elem()
newty := reflect.TypeOf(newi.Addr().Interface()).Elem()
for i := 0; i < nums-1; i++ {
newi.Field(i).Set(fieldMap[newty.Field(i).Name])
}
return newi.Addr().Interface()
}
func getJavaClassName(pkg interface{}) string {
val := reflect.ValueOf(pkg).Elem()
typ := reflect.TypeOf(pkg).Elem()
nums := val.NumField()
for i := 0; i < nums; i++ {
if typ.Field(i).Name == "JavaClassName" {
return val.Field(i).String()
}
}
fmt.Println("error: JavaClassName not found")
return ""
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package json_struct_parser
import (
"testing"
)
import (
"github.com/stretchr/testify/assert"
)
func Test_newJsonStructParser(t *testing.T) {
path := "./user.json"
jparser := newJsonStructParser()
_, err := jparser.Jsonfile2Struct(path)
assert.Nil(t, err)
}
{
"ID": "string",
"Name": "string",
"Age": "int",
"JavaClassName": "string@com.ikurento.user.User",
"SubInfo": {
"SubID": "string",
"SubMale": "bool",
"SubAge": "int",
"JavaClassName":"string@com.ikurento.user.SubInfo"
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment