del: 删除json转结构体老的方法 (#108)

This commit is contained in:
jefferyjob
2025-08-16 19:54:28 +08:00
committed by GitHub
parent e438f475b4
commit c2cd2b2c81
26 changed files with 8 additions and 2283 deletions

View File

@@ -1,100 +0,0 @@
# JsonToStruct 开发文档
## 处理指针类型
```go
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return 0, nil // 检查解引用后的值是否为 nil
}
v = v.Elem() // 获取指针所指向的值
}
```
`reflect`包中,指针类型和其指向的值的类型是不同的。例如,一个指向`int`类型的指针,其类型是`*int`,而其指向的值的类型是`int`。因此,在使用`reflect`包时,我们需要注意区分指针类型和其指向的值的类型,否则可能会出现类型错误等问题。
因此,对于`value`为指针类型的情况我们需要通过Elem方法获取其指向的值然后再进行类型转换。而在使用`Elem`方法之前我们需要先检查v是否为指针类型因此使用循环语句进行判断。
**举个例子**
```go
type Person struct {
Name string
Age int
}
func main() {
p := &Person{"Tom", 18}
v := reflect.ValueOf(p).Elem()
name := v.FieldByName("Name")
age := v.FieldByName("Age")
fmt.Println(name.String()) // 输出Tom
fmt.Println(age.Int()) // 输出18
name.SetString("Jerry") // 修改 Name 字段的值
age.SetInt(20) // 修改 Age 字段的值
fmt.Println(p.Name) // 输出Jerry
fmt.Println(p.Age) // 输出20
}
```
在上面的例子中,我们首先将结构体指针 `p` 转换为 `reflect.Value` 类型的值 `v`,然后使用 `v.Elem()` 获取指针所指向的值,接着使用 `FieldByName()` 方法获取结构体字段的 `reflect.Value` 类型的值,最后使用 `SetString()``SetInt()` 方法修改结构体字段的值。
## 处理Slice数据类型
```go
case reflect.Slice:
if subData, ok := value.([]interface{}); ok {
if err := parseSlice(fieldValue, subData); err != nil {
return err
}
} else {
return fmt.Errorf("unexpected value type for slice: %T", value)
}
```
将每个子元素都转换为结构体定义的数据类型再赋值给对应的字段。如果子元素是一个切片类型,需要进行递归处理。
首先获取到切片的元素类型 subType如果 subType 是一个指针类型,需要将其转换为实际的元素类型。然后创建一个与原始切片相同长度的新切片 subSliceValue并循环遍历原始切片中的每个子元素。在每个子元素中需要将其转换为结构体定义的数据类型可以通过递归调用 parseStruct 或 parseSlice 来实现。最后,将新的子元素设置到新切片 subSliceValue 中,并将其赋值给对应的字段。注意,如果元素类型是一个结构体,需要使用 Set 方法设置到新切片中,如果是基本类型,则需要判定类型然后添加到值中。
## 处理Map数据类型
**第一种更加简洁的调用方法**
```go
case reflect.Map:
if subData, ok := value.(map[string]interface{}); ok {
subResult := reflect.New(fieldType.Type).Elem()
if err := parseMap(subResult, subData); err != nil {
return err
}
fieldValue.Set(subResult)
}
```
其中 subResult 使用 reflect.New 创建一个新的结构体对象,然后使用 Elem() 方法获取其指针所指向的值,最后传递给 parseMap 方法进行解析。这样可以避免对 reflect.MakeMap 的使用,更加简洁。
**第二种实在原有的代码进行更改。**
```go
case reflect.Map:
if subData, ok := value.(map[string]interface{}); ok {
subResult := reflect.MakeMap(fieldValue.Type())
for subKey, subValue := range subData {
subElem := reflect.New(fieldType.Type.Elem())
err := parseValue(subElem.Elem(), subValue)
if err != nil {
return err
}
subResult.SetMapIndex(reflect.ValueOf(subKey), subElem.Elem())
}
fieldValue.Set(subResult)
}
```
其中subData 是一个 map[string]interface{} 类型的变量,表示一个嵌套的 JSON 对象subResult 是一个 reflect.Value 类型的变量,表示一个 map 类型的变量的反射值subKey 和 subValue 分别表示 subData 中的键和值,可以通过 range 循环遍历 subData。
在调用 parseValue 函数时,需要传入 subElem.Elem() 作为第一个参数,这是因为 subElem 表示一个指向 fieldType.Type.Elem() 类型变量的指针,而 parseValue 函数需要传入一个值类型的变量。所以需要通过 subElem.Elem() 获取到 fieldType.Type.Elem() 类型的变量。

View File

@@ -1,112 +0,0 @@
# jsonUtil
[English](README.md) | 简体中文
## 介绍
JsonToStruct 将 JSON 数据转换为 Go 结构。
**参数:**
- jsonData包含 JSON 数据的字符串。
- val指向要填充的结构变量的指针。
**返回:**
- error如果转换失败或发生错误则返回相应的错误。如果成功则返回 nil。
**功能:**
- 检查 val 参数是否为非零指针类型,如果不是,则返回 ErrPoint。
- 将 jsonData 解析为名为 data 的 map[string]any 变量。
- 使用反射检索 val 指向的结构的值和类型。
- 遍历结构的字段:
- 检索字段的类型、名称和值。
- 获取字段的 JSON 标签。
- 如果数据中存在与 JSON 标签对应的键值对,则进行相应的处理:
- 如果字段是原始类型string、integer、float、boolean则解析为对应类型的值。
- 如果字段是结构体类型,则递归调用 JsonToStruct 函数将子结构体转换为 JSON。
- 如果字段是映射类型,则使用 parseMap 函数将子映射转换为 JSON。
- 如果字段是切片类型,则使用 parseSlice 函数将子切片转换为 JSON。
- 如果字段是接口类型,则将值设置为 nil 或对应值。
## 安装
```bash
go get -u github.com/jefferyjob/go-easy-utils/v3/jsonx
```
## 导入
```go
import (
"github.com/jefferyjob/go-easy-utils/v3/jsonx"
)
```
## 方法
```go
func JsonToStruct(jsonData string, val any) error
```
## Demo
```go
// name 使用了两个 json 标签
// age 定义为 intjson 的值是 string
// is_use 定义为 booljson 的值是 int
func TestDemo1(t *testing.T) {
jsonData := `{
"name": "make",
"age": "22",
"is_use": "1"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
if err := ToStruct(jsonData, &people); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", people)
// return
// {Name:make Age:22 IsUse:true}
}
```
```go
// 结构嵌套和切片嵌套处理
func TestJsonToStructDemo2(t *testing.T) {
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
Interests []string `json:"interests"`
}
jsonData := `{
"name": "Bob",
"age": "25",
"address": {
"city": "Shanghai",
"country": "China"
},
"interests": ["reading", "swimming"]
}`
var person Person
err := ToStruct(jsonData, &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", person)
// {Name:Bob Age:25 Address:{City:Shanghai Country:China} Interests:[reading swimming]}
}
```

View File

@@ -1,112 +0,0 @@
# jsonx
English | [简体中文](README.cn.md)
## Introduce
JsonToStruct converts JSON data to a Go structure.
**Parameters:**
- jsonData: A string containing the JSON data.
- val: A pointer to the structure variable to be filled.
**Returns:**
- error: If conversion fails or an error occurs, the corresponding error is returned. If successful, nil is returned.
**Functionality:**
- Checks if the val parameter is a non-nil pointer type, returning ErrPoint if it is not.
- Parses jsonData into a map[string]any variable called data.
- Retrieves the value and type of the structure pointed to by val using reflection.
- Iterates through the fields of the structure:
- Retrieves the field's type, name, and value.
- Gets the JSON tag for the field.
- Performs the appropriate handling if a key-value pair corresponding to the JSON tag exists in data:
- If the field is a primitive type (string, integer, float, boolean), parses it into the corresponding type's value.
- If the field is a struct type, recursively calls the JsonToStruct function to convert the sub-structure to JSON.
- If the field is a map type, uses the parseMap function to convert the sub-map to JSON.
- If the field is a slice type, uses the parseSlice function to convert the sub-slice to JSON.
- If the field is an interface type, sets the value to nil or the corresponding value.
## Install
```bash
go get -u github.com/jefferyjob/go-easy-utils/v3/jsonx
```
## Import
```go
import (
"github.com/jefferyjob/go-easy-utils/v3/jsonx"
)
```
## Functions
```go
func ToStruct(jsonData string, val any) error
```
## Demo
```go
// name uses two json tags
// age is defined as int, and the value of json is string
// is_use is defined as bool, the value of json is int
func TestDemo1(t *testing.T) {
jsonData := `{
"name": "make",
"age": "22",
"is_use": "1"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
if err := ToStruct(jsonData, &people); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", people)
// return
// {Name:make Age:22 IsUse:true}
}
```
```go
// Structure nesting and slice nesting processing
func TestJsonToStructDemo2(t *testing.T) {
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
Interests []string `json:"interests"`
}
jsonData := `{
"name": "Bob",
"age": "25",
"address": {
"city": "Shanghai",
"country": "China"
},
"interests": ["reading", "swimming"]
}`
var person Person
err := ToStruct(jsonData, &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", person)
// {Name:Bob Age:25 Address:{City:Shanghai Country:China} Interests:[reading swimming]}
}
```

View File

@@ -2,17 +2,18 @@ package jsonx
import (
"errors"
"strconv"
)
var (
// ErrPoint 不是指针类型
// ErrPoint = errors.New("the argument to Result must be a non-nil pointer")
// // ErrNotMap 不是Map类型
// ErrNotMap = errors.New("cannot parse map, value is not a map")
// // ErrNotSlice 不是Slice类型
// ErrNotSlice = errors.New("cannot parse slice, value is not a slice")
// // ErrSyntax 指示值不具有目标类型的正确语法
// ErrSyntax = strconv.ErrSyntax
ErrPoint = errors.New("the argument to Result must be a non-nil pointer")
// ErrNotMap 不是Map类型
ErrNotMap = errors.New("cannot parse map, value is not a map")
// ErrNotSlice 不是Slice类型
ErrNotSlice = errors.New("cannot parse slice, value is not a slice")
// ErrSyntax 指示值不具有目标类型的正确语法
ErrSyntax = strconv.ErrSyntax
// ErrUnsupported 不支持的类型
ErrUnsupported = errors.New("unsupported type")
)

View File

@@ -1,103 +0,0 @@
package jsonx
import (
"fmt"
"reflect"
)
func parseMap(value reflect.Value, data map[string]any) error {
if value.Kind() != reflect.Map {
return ErrNotMap
}
valueType := value.Type().Elem()
newMap := reflect.MakeMapWithSize(value.Type(), len(data))
for key, val := range data {
newKey := reflect.ValueOf(key)
if valueType.Kind() == reflect.Interface {
newMap.SetMapIndex(newKey, reflect.ValueOf(val))
} else if valueType.Kind() == reflect.Map {
newElem := reflect.New(valueType).Elem()
if err := parseMap(newElem, val.(map[string]any)); err != nil {
return err
}
newMap.SetMapIndex(newKey, newElem)
} else {
newVal := reflect.New(valueType).Elem()
if err := parseValue(newVal, val); err != nil {
return err
}
newMap.SetMapIndex(newKey, newVal)
}
}
value.Set(newMap)
return nil
}
func parseValue(fieldVal reflect.Value, item any) error {
switch fieldVal.Kind() {
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.Bool:
if err := parsePrimitiveValue(fieldVal, item); err != nil {
return err
}
case reflect.Struct:
if subData, ok := item.(map[string]any); ok {
if err := ToStruct(convertToJSONString(subData), fieldVal.Addr().Interface()); err != nil {
return err
}
} else {
return fmt.Errorf("unexpected value type for struct: %T", item)
}
case reflect.Slice:
if arr, ok := item.([]any); ok {
elemType := fieldVal.Type().Elem()
sliceVal := reflect.MakeSlice(fieldVal.Type(), len(arr), len(arr))
for i, elem := range arr {
elemVal := reflect.New(elemType).Elem()
if err := parseValue(elemVal, elem); err != nil {
return err
}
sliceVal.Index(i).Set(elemVal)
}
fieldVal.Set(sliceVal)
} else {
return fmt.Errorf("unexpected value type for slice: %T", item)
}
case reflect.Map:
if mapData, ok := item.(map[string]any); ok {
mapType := fieldVal.Type()
if fieldVal.IsNil() {
fieldVal.Set(reflect.MakeMap(mapType))
}
elemType := mapType.Elem()
mapVal := fieldVal
for k, v := range mapData {
keyVal := reflect.New(mapType.Key()).Elem()
if err := parseValue(keyVal, k); err != nil {
return err
}
elemVal := reflect.New(elemType).Elem()
if err := parseValue(elemVal, v); err != nil {
return err
}
mapVal.SetMapIndex(keyVal, elemVal)
}
} else {
return fmt.Errorf("unexpected value type for map: %T", item)
}
case reflect.Interface:
if item == nil {
fieldVal.Set(reflect.Zero(fieldVal.Type()))
} else {
fieldVal.Set(reflect.ValueOf(item))
}
default:
return fmt.Errorf("unsupported kind: %s", fieldVal.Kind())
}
return nil
}

View File

@@ -1,91 +0,0 @@
package jsonx
import (
"reflect"
"testing"
)
func TestParseMap(t *testing.T) {
type TestData struct {
Foo string
Bar int
}
// 包含映射类型的测试用例
testData := map[string]any{
"Foo": "hello",
"Bar": 42,
}
var result map[string]any
err := parseMap(reflect.ValueOf(&result).Elem(), testData)
if err != nil {
t.Errorf("parseMap 失败: %s", err)
}
// 检查值是否被正确解析
expectedResult := map[string]any{"Foo": "hello", "Bar": 42}
if !reflect.DeepEqual(result, expectedResult) {
t.Errorf("parseMap 结果不匹配:\n期望值: %v\n实际值: %v", expectedResult, result)
}
}
func TestParseValue(t *testing.T) {
// 测试解析原始类型
var intValue int
err := parseValue(reflect.ValueOf(&intValue).Elem(), 42)
if err != nil {
t.Errorf("parseValue 失败: %s", err)
}
if intValue != 42 {
t.Errorf("parseValue 结果不匹配: 期望值 42实际值 %d", intValue)
}
// 测试解析结构体
type TestStruct struct {
Name string
Age int
}
var structValue TestStruct
err = parseValue(reflect.ValueOf(&structValue).Elem(), map[string]any{"Name": "John", "Age": 30})
if err != nil {
t.Errorf("parseValue 失败: %s", err)
}
expectedStructValue := TestStruct{"John", 30}
if !reflect.DeepEqual(structValue, expectedStructValue) {
t.Errorf("parseValue 结果不匹配:\n期望值: %v\n实际值: %v", expectedStructValue, structValue)
}
// 测试解析切片
var sliceValue []int
err = parseValue(reflect.ValueOf(&sliceValue).Elem(), []any{1, 2, 3})
if err != nil {
t.Errorf("parseValue 失败: %s", err)
}
expectedSliceValue := []int{1, 2, 3}
if !reflect.DeepEqual(sliceValue, expectedSliceValue) {
t.Errorf("parseValue 结果不匹配:\n期望值: %v\n实际值: %v", expectedSliceValue, sliceValue)
}
// 测试解析接口
var interfaceValue interface{}
err = parseValue(reflect.ValueOf(&interfaceValue).Elem(), "test")
if err != nil {
t.Errorf("parseValue 失败: %s", err)
}
expectedInterfaceValue := "test"
if !reflect.DeepEqual(interfaceValue, expectedInterfaceValue) {
t.Errorf("parseValue 结果不匹配:\n期望值: %v\n实际值: %v", expectedInterfaceValue, interfaceValue)
}
// 测试不支持的类型
var unsupportedValue complex64
err = parseValue(reflect.ValueOf(&unsupportedValue).Elem(), 3.14)
if err == nil {
t.Errorf("parseValue 对于不支持的类型应该失败")
}
}

View File

@@ -1,38 +0,0 @@
package jsonx
import (
"fmt"
"reflect"
)
// 原始数据类型转换支持
// 将数据转为Struct指定的类型
func parsePrimitiveValue(fieldVal reflect.Value, v any) error {
switch fieldVal.Kind() {
case reflect.String:
fieldVal.SetString(toStringReflect(v))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := toInt64Reflect(v)
if err != nil {
return err
}
fieldVal.SetInt(n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
n, err := toUint64Reflect(v)
if err != nil {
return err
}
fieldVal.SetUint(uint64(n))
case reflect.Float32, reflect.Float64:
n, err := toFloat64Reflect(v)
if err != nil {
return err
}
fieldVal.SetFloat(n)
case reflect.Bool:
fieldVal.SetBool(toBoolReflect(v))
default:
return fmt.Errorf("unsupported kind: %s", fieldVal.Kind())
}
return nil
}

View File

@@ -1,84 +0,0 @@
package jsonx
import (
"reflect"
"testing"
)
func TestParsePrimitiveValue(t *testing.T) {
// 测试 reflect.String 类型的情况
strFieldVal := reflect.ValueOf(new(string)).Elem()
err := parsePrimitiveValue(strFieldVal, "hello")
if err != nil {
t.Errorf("Error parsing string value: %v", err)
}
if strFieldVal.String() != "hello" {
t.Errorf("Expected %q, got %q", "hello", strFieldVal.String())
}
err = parsePrimitiveValue(strFieldVal, 123)
if err != nil {
t.Error("Expected error for parsing int value as string")
}
// 测试 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64 类型的情况
intFieldVal := reflect.ValueOf(new(int64)).Elem()
err = parsePrimitiveValue(intFieldVal, "123")
if err != nil {
t.Errorf("Error parsing int value: %v", err)
}
if intFieldVal.Int() != 123 {
t.Errorf("Expected %d, got %d", 123, intFieldVal.Int())
}
err = parsePrimitiveValue(intFieldVal, "abc")
if err == nil {
t.Error("Expected error for parsing non-int value as int")
}
// 测试 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64 类型的情况
uintFieldVal := reflect.ValueOf(new(uint64)).Elem()
err = parsePrimitiveValue(uintFieldVal, "123")
if err != nil {
t.Errorf("Error parsing uint value: %v", err)
}
if uintFieldVal.Uint() != 123 {
t.Errorf("Expected %d, got %d", 123, uintFieldVal.Uint())
}
err = parsePrimitiveValue(uintFieldVal, 123)
if err != nil {
t.Errorf("Expected error: %v", err)
}
// 测试 reflect.Float32, reflect.Float64 类型的情况
floatFieldVal := reflect.ValueOf(new(float64)).Elem()
err = parsePrimitiveValue(floatFieldVal, "3.14")
if err != nil {
t.Errorf("Error parsing float value: %v", err)
}
if floatFieldVal.Float() != 3.14 {
t.Errorf("Expected %f, got %f", 3.14, floatFieldVal.Float())
}
err = parsePrimitiveValue(floatFieldVal, "abc")
if err == nil {
t.Error("Expected error for parsing non-float value as float")
}
// 测试 reflect.Bool 类型的情况
boolFieldVal := reflect.ValueOf(new(bool)).Elem()
err = parsePrimitiveValue(boolFieldVal, true)
if err != nil {
t.Errorf("Error parsing bool value: %v", err)
}
if boolFieldVal.Bool() != true {
t.Errorf("Expected %t, got %t", true, boolFieldVal.Bool())
}
err = parsePrimitiveValue(boolFieldVal, "abc")
if err != nil {
t.Error("Expected error for parsing non-bool value as bool")
}
// 测试不支持的类型的情况
unsupportedFieldVal := reflect.ValueOf(new([]string)).Elem()
err = parsePrimitiveValue(unsupportedFieldVal, "abc")
if err == nil {
t.Error("Expected error for unsupported kind")
}
}

View File

@@ -1,65 +0,0 @@
package jsonx
import (
"encoding/json"
"fmt"
"reflect"
)
func parseSlice(fieldValue reflect.Value, subData []any) error {
if fieldValue.Kind() != reflect.Slice {
return ErrNotSlice
}
// 获取切片元素类型
elemType := fieldValue.Type().Elem()
// 创建一个与目标字段相同类型的新切片
slice := reflect.MakeSlice(fieldValue.Type(), 0, len(subData))
// 迭代 subData 切片的元素
for _, item := range subData {
// 创建 slice 元素类型的新元素
elem := reflect.New(elemType).Elem()
// 如果元素类型是结构体,则递归调用 JsonToStruct
if elem.Kind() == reflect.Struct {
// 将项目转换为 JSON 字符串
jsonData, err := json.Marshal(item)
if err != nil {
return err
}
// 调用 ToStruct 将 JSON 字符串解析为 struct 元素
err = ToStruct(string(jsonData), elem.Addr().Interface())
if err != nil {
return err
}
} else if elemType.Kind() == reflect.Interface {
if item == nil {
elem.Set(reflect.Zero(elemType))
} else {
elem.Set(reflect.ValueOf(item))
}
} else {
// 否则,将项目转换为适当的类型并设置元素值
switch elem.Kind() {
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.Bool:
err := parsePrimitiveValue(elem, item)
if err != nil {
return err
}
default:
return fmt.Errorf("unsupported slice element type: %s", elemType.String())
}
}
slice = reflect.Append(slice, elem) // 将元素附加到切片
}
fieldValue.Set(slice) // 将切片值设置为目标字段
return nil
}

View File

@@ -1,56 +0,0 @@
package jsonx
import (
"reflect"
"testing"
)
type ExampleStruct struct {
Name string `json:"name"`
Age int `json:"age"`
}
func TestParseSlice(t *testing.T) {
var slice []any
// 添加元素到切片
slice = append(slice, "hello")
slice = append(slice, "world")
// 创建一个新的切片值
sliceValue := reflect.ValueOf(&[]string{}).Elem()
// 解析切片
err := parseSlice(sliceValue, slice)
if err != nil {
t.Errorf("expected no error, but got %s", err.Error())
}
// 检查解析的切片是否与预期相等
expectedSlice := []string{"hello", "world"}
actualSlice := sliceValue.Interface().([]string)
if !reflect.DeepEqual(expectedSlice, actualSlice) {
t.Errorf("expected %v, but got %v", expectedSlice, actualSlice)
}
// 测试解析结构体切片
var structSlice []any
structSlice = append(structSlice, ExampleStruct{"Tom", 30})
structSlice = append(structSlice, ExampleStruct{"Jerry", 25})
// 创建一个新的切片值
structSliceValue := reflect.ValueOf(&[]ExampleStruct{}).Elem()
// 解析切片
err = parseSlice(structSliceValue, structSlice)
if err != nil {
t.Errorf("expected no error, but got %s", err.Error())
}
// 检查解析的切片是否与预期相等
expectedStructSlice := []ExampleStruct{{"Tom", 30}, {"Jerry", 25}}
actualStructSlice := structSliceValue.Interface().([]ExampleStruct)
if !reflect.DeepEqual(expectedStructSlice, actualStructSlice) {
t.Errorf("expected %v, but got %v", expectedStructSlice, actualStructSlice)
}
}

View File

@@ -1,44 +0,0 @@
package jsonx
import (
"reflect"
)
func toBoolReflect(i any) bool {
if i == nil {
return false
}
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return false
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Bool:
return v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() != 0
case reflect.Float32, reflect.Float64:
return v.Float() != 0
case reflect.Complex64, reflect.Complex128:
return v.Complex() != 0
case reflect.String:
val := v.String()
if val == "true" {
return true
} else if val == "false" {
return false
}
return val != ""
// case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice:
// return !v.IsNil()
default:
return false
}
}

View File

@@ -1,62 +0,0 @@
package jsonx
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestToBool(t *testing.T) {
var iPtr = 90
var tests = []struct {
name string
input any
want bool
}{
{"布尔真", true, true},
{"布尔假", false, false},
{"负整数", int(-1), true},
{"正整数", int(1), true},
{"零整数", int(0), false},
{"正int8", int8(1), true},
{"零int8", int8(0), false},
{"正int16", int16(1), true},
{"零int16", int16(0), false},
{"正int32", int32(1), true},
{"零int32", int32(0), false},
{"正int64", int64(1), true},
{"零int64", int64(0), false},
{"正uint", uint(1), true},
{"零uint", uint(0), false},
{"正uint8", uint8(1), true},
{"零uint8", uint8(0), false},
{"正uint16", uint16(1), true},
{"零uint16", uint16(0), false},
{"正uint32", uint32(1), true},
{"零uint32", uint32(0), false},
{"正uint64", uint64(1), true},
{"零uint64", uint64(0), false},
{"浮点1.0", float32(1.0), true},
{"浮点0.0", float32(0.0), false},
{"双精1.0", float64(1.0), true},
{"双精0.0", float64(0.0), false},
{"字符串", "abc", true},
{"字符串真", "true", true},
{"字符串假", "false", false},
{"空字符串", "", false},
{"空值", nil, false},
{"非空指针", &iPtr, true},
{"复数1+1i", complex64(1 + 1i), true},
{"复数0+0i", complex64(0 + 0i), false},
{"双复1+1i", complex128(1 + 1i), true},
{"双复0+0i", complex128(0 + 0i), false},
{"空指针", (*int)(nil), false},
{"通道", make(chan int), false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
res := toBoolReflect(tc.input)
assert.Equal(t, tc.want, res)
})
}
}

View File

@@ -1,50 +0,0 @@
package jsonx
import (
"reflect"
"strconv"
)
func toFloat64Reflect(i any) (float64, error) {
if i == nil {
return 0, nil
}
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return 0, nil
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Float32, reflect.Float64:
return v.Float(), nil
case reflect.String:
if v.String() == "" {
return 0, nil
}
floatValue, err := strconv.ParseFloat(v.String(), 64)
if err != nil {
return 0, ErrSyntax
}
return floatValue, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(v.Uint()), nil
case reflect.Complex64, reflect.Complex128:
return real(v.Complex()), nil
case reflect.Bool:
if v.Bool() {
return 1, nil
} else {
return 0, nil
}
// case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice:
// return 0, nil
default:
return 0, ErrType
}
}

View File

@@ -1,46 +0,0 @@
package jsonx
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestToFloat64(t *testing.T) {
var iPtr = 90
testCases := []struct {
name string
value any
expected float64
expectedErr error
}{
{"空值", nil, 0, nil},
{"浮点数", float32(123.5), 123.5, nil},
{"字符串数", "123.456", 123.456, nil},
{"无符整型", uint(123), 123, nil},
{"无符uint8", uint8(123), 123, nil},
{"无符uint16", uint16(123), 123, nil},
{"无符uint32", uint32(123), 123, nil},
{"无符uint64", uint64(123), 123, nil},
{"有符整型", int(123), 123, nil},
{"有符int8", int8(123), 123, nil},
{"有符int16", int16(123), 123, nil},
{"有符int32", int32(123), 123, nil},
{"有符int64", int64(123), 123, nil},
{"复数64", complex64(1 + 2i), 1, nil},
{"复数128", complex128(1 + 2i), 1, nil},
{"布尔真", true, 1, nil},
{"布尔假", false, 0, nil},
{"空布尔指针", (*bool)(nil), 0, nil},
{"非空指针", &iPtr, 90, nil},
{"通道", make(chan int), 0, ErrType},
{"无效字符串", "abc", 0, ErrSyntax},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res, err := toFloat64Reflect(tc.value)
assert.Equal(t, tc.expectedErr, err)
assert.Equal(t, tc.expected, res)
})
}
}

View File

@@ -1,50 +0,0 @@
package jsonx
import (
"reflect"
"strconv"
)
func toInt64Reflect(i any) (int64, error) {
if i == nil {
return 0, nil
}
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return 0, nil
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Float32, reflect.Float64:
return int64(v.Float()), nil
case reflect.String:
if v.String() == "" {
return 0, nil
}
intValue, err := strconv.ParseInt(v.String(), 10, 64)
if err != nil {
return 0, ErrSyntax
}
return intValue, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return int64(v.Uint()), nil
case reflect.Complex64:
return int64(real(v.Complex())), nil
case reflect.Complex128:
return int64(real(v.Complex())), nil
case reflect.Bool:
if v.Bool() {
return 1, nil
} else {
return 0, nil
}
default:
return 0, ErrType
}
}

View File

@@ -1,58 +0,0 @@
package jsonx
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestAnyToInt64(t *testing.T) {
var iPtr = 90
testCases := []struct {
name string
input any
expected int64
err error
}{
// 测试整数输入
{name: "number", input: 42, expected: 42, err: nil},
{name: "int", input: int(42), expected: 42, err: nil},
{name: "int64", input: int64(42), expected: 42, err: nil},
{name: "int32", input: int32(42), expected: 42, err: nil},
{name: "int", input: int(42), expected: 42, err: nil},
{name: "int64", input: uint64(42), expected: 42, err: nil},
{name: "int32", input: uint32(42), expected: 42, err: nil},
{name: "uint", input: uint(42), expected: 42, err: nil},
// 测试浮点数输入
{name: "float64", input: float64(42.0), expected: 42, err: nil},
{name: "float32", input: float32(42.0), expected: 42, err: nil},
{name: "float64_小数点", input: float64(42.5), expected: 42, err: nil},
// 测试非数值类型
{name: "string", input: "rand string", expected: 0, err: ErrSyntax},
{name: "nil", input: nil, expected: 0, err: nil},
// 布尔
{name: "true", input: true, expected: 1, err: nil},
{name: "false", input: false, expected: 0, err: nil},
// 空指针
{name: "nil point", input: (*int)(nil), expected: 0, err: nil},
{name: "point", input: &iPtr, expected: 90, err: nil},
// complex
{name: "complex128", input: complex(3.14, 1.59), expected: 3, err: nil},
{name: "complex64", input: complex(float32(2.71), float32(1.41)), expected: 2, err: nil},
// 其他
{name: "struct", input: struct{ Name string }{Name: "test"}, expected: 0, err: ErrType},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
res, err := toInt64Reflect(tc.input)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.expected, res)
})
}
}

View File

@@ -1,42 +0,0 @@
package jsonx
import (
"fmt"
"reflect"
"strconv"
)
func toStringReflect(i any) string {
if i == nil {
return ""
}
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return ""
}
v = v.Elem()
}
switch v.Kind() {
case reflect.String:
return v.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(v.Uint(), 10)
case reflect.Float32:
return strconv.FormatFloat(v.Float(), 'f', -1, 32)
case reflect.Float64:
return strconv.FormatFloat(v.Float(), 'f', -1, 64)
case reflect.Complex64:
return fmt.Sprintf("(%g+%gi)", real(v.Complex()), imag(v.Complex()))
case reflect.Complex128:
return fmt.Sprintf("(%g+%gi)", real(v.Complex()), imag(v.Complex()))
case reflect.Bool:
return strconv.FormatBool(v.Bool())
default:
return ""
}
}

View File

@@ -1,43 +0,0 @@
package jsonx
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestToString(t *testing.T) {
var iPar = "point"
tests := []struct {
name string
value any
want string
}{
{"nil", nil, ""},
{"string", "hello", "hello"},
{"int", 42, "42"},
{"int8", int8(42), "42"},
{"int16", int16(42), "42"},
{"int32", int32(42), "42"},
{"int64", int64(42), "42"},
{"uint", uint(42), "42"},
{"uint8", uint8(42), "42"},
{"uint16", uint16(42), "42"},
{"uint32", uint32(42), "42"},
{"uint64", uint64(42), "42"},
{"float32", float32(3.14159), "3.14159"},
{"float64", 3.14159, "3.14159"},
{"bool-true", true, "true"},
{"bool-false", false, "false"},
{"point", &iPar, "point"},
{"complex64", complex64(1 + 2i), "(1+2i)"},
{"complex128", complex128(3 + 4i), "(3+4i)"},
{"chan", make(chan int), ""},
{"int nil", (*int)(nil), ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := toStringReflect(tt.value)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,120 +0,0 @@
package jsonx
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
// ToStruct converts JSON data to a Go structure.
// Parameters:
//
// jsonData: A string containing the JSON data.
// val: A pointer to the structure variable to be filled.
//
// Returns:
//
// error: If conversion fails or an error occurs, the corresponding error is returned. If successful, nil is returned.
//
// Functionality:
//
// Checks if the val parameter is a non-nil pointer type, returning ErrPoint if it is not.
// Parses jsonData into a map[string]any variable called data.
// Retrieve the value and type of the structure pointed to by val using reflection.
// Iterates through the fields of the structure:
// Retrieves the field's type, name, and value.
// Get the JSON tag for the field.
// Performs the appropriate handling if a key-value pair corresponding to the JSON tag exists in data:
// If the field is a primitive type (string, integer, float, boolean), parses it into the corresponding type's value.
// If the field is a struct type, recursively calls the JsonToStruct function to convert the sub-structure to JSON.
// If the field is a map type, uses the parseMap function to convert the sub-map to JSON.
// If the field is a slice type, uses the parseSlice function to convert the sub-slice to JSON.
// If the field is an interface type, sets the value to nil or the corresponding value.
func ToStruct(jsonData string, val any) error {
if reflect.ValueOf(val).Kind() != reflect.Pointer || reflect.ValueOf(val).IsNil() {
return ErrPoint
}
var data map[string]any
err := json.Unmarshal([]byte(jsonData), &data)
if err != nil {
return err
}
resultValue := reflect.ValueOf(val).Elem()
resultType := resultValue.Type()
for i := 0; i < resultType.NumField(); i++ {
fieldType := resultType.Field(i)
fieldName := fieldType.Name
fieldValue := resultValue.FieldByName(fieldName)
// 获取json的tag
jsonTag := getJsonTag(fieldType, fieldName)
value, ok := data[jsonTag]
if !ok {
continue
}
switch fieldValue.Kind() {
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64, reflect.Bool:
if err := parsePrimitiveValue(fieldValue, value); err != nil {
return err
}
case reflect.Struct:
if subData, ok := value.(map[string]any); ok {
subResult := reflect.New(fieldValue.Type())
err := ToStruct(convertToJSONString(subData), subResult.Interface())
if err != nil {
return err
}
fieldValue.Set(subResult.Elem())
}
case reflect.Map:
if subData, ok := value.(map[string]any); ok {
subResult := reflect.New(fieldType.Type).Elem()
if err := parseMap(subResult, subData); err != nil {
return err
}
fieldValue.Set(subResult)
}
case reflect.Slice:
if subData, ok := value.([]any); ok {
if err := parseSlice(fieldValue, subData); err != nil {
return err
}
} else {
return fmt.Errorf("unexpected value type for slice: %T", value)
}
case reflect.Interface:
if value == nil {
fieldValue.Set(reflect.Zero(fieldType.Type))
} else {
fieldValue.Set(reflect.ValueOf(value))
}
}
}
return nil
}
// 从json的tag标签中取出定义字段
func getJsonTag(fieldType reflect.StructField, fieldName string) string {
jsonTag := fieldType.Tag.Get("json")
if jsonTag == "" {
jsonTag = fieldName
} else {
if commaIndex := strings.Index(jsonTag, ","); commaIndex != -1 {
jsonTag = jsonTag[:commaIndex]
}
}
return jsonTag
}
func convertToJSONString(data map[string]any) string {
jsonBytes, _ := json.Marshal(data)
return string(jsonBytes)
}

View File

@@ -1,26 +0,0 @@
package jsonx
import "fmt"
func ExampleToStruct() {
jsonData := `{
"name": "make",
"age": "22",
"is_use": "1"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
if err := ToStruct(jsonData, &people); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v", people)
// Output:
// {Name:make Age:22 IsUse:true}
}

View File

@@ -1,71 +0,0 @@
package jsonx
import (
"testing"
)
func TestJsonToStructMap1(t *testing.T) {
data := `
{
"uid": 43015653,
"foll": {
"43015653": true,
"43015666": false
},
"followed": {
"friendRed": {
"43015653": true,
"43015666": false
},
"friendWhite": {
"43015653": true,
"43015666": false
}
}
}
`
var target struct {
Uid int `json:"uid"`
Foll map[string]bool `json:"foll"`
Followed map[string]map[string]bool `json:"followed"`
}
err := ToStruct(data, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
func TestJsonToStructMap2(t *testing.T) {
data := `
{
"uid": 43015653,
"foll": {
"boy": {
"t1": "v1",
"t2": "v2"
},
"girl": {
"t1": "v1",
"t2": "v2"
}
}
}
`
type Val struct {
T1 string `json:"t1"`
T2 string `json:"t2"`
}
var target struct {
Uid int `json:"uid"`
Foll map[string]Val `json:"foll"`
}
err := ToStruct(data, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}

View File

@@ -1,24 +0,0 @@
package jsonx
import (
"testing"
)
func TestJsonToStructSlice1(t *testing.T) {
data := `
{
"uid": 43015653,
"fids": ["43015653", 43015666]
}
`
var target struct {
Uid int `json:"uid"`
Fids []string `json:"fids"`
}
err := ToStruct(data, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}

View File

@@ -1,629 +0,0 @@
package jsonx
import (
"encoding/json"
"fmt"
"reflect"
"testing"
)
// name uses two json tags
// age is defined as int, and the value of json is string
// is_use is defined as bool, the value of json is int
func TestDemo1(t *testing.T) {
jsonData := `{
"name": "make",
"age": "22",
"is_use": "1"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
if err := ToStruct(jsonData, &people); err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", people)
// return
// {Name:make Age:22 IsUse:true}
}
// Structure nesting and slice nesting processing
func TestJsonToStructDemo2(t *testing.T) {
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
Interests []string `json:"interests"`
}
jsonData2 := `{
"name": "Bob",
"age": "25",
"address": {
"city": "Shanghai",
"country": "China"
},
"interests": ["reading", "swimming"]
}`
var person Person
err := ToStruct(jsonData2, &person)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v \n", person)
// {Name:Bob Age:25 Address:{City:Shanghai Country:China} Interests:[reading swimming]}
}
func BenchmarkJsonUnmarshal(b *testing.B) {
jsonData := `{
"name": "make",
"age": 22,
"is_use": true
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
for i := 0; i < b.N; i++ {
err := json.Unmarshal([]byte(jsonData), &people)
if err != nil {
fmt.Println("err:", err)
return
}
}
}
func BenchmarkJsonToStruct(b *testing.B) {
jsonData := `{
"name": "make",
"age": "22",
"is_use": true
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
for i := 0; i < b.N; i++ {
err := ToStruct(jsonData, &people)
if err != nil {
fmt.Println("err:", err)
return
}
}
}
// 非法验证非法的json字符串
func TestJsonToStructErrJson(t *testing.T) {
jsonData := `{"name":}`
type People struct {
Name string `json:"name"`
}
var people People
err := ToStruct(jsonData, &people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 非法验证非指针的result
func TestJsonToStructErrResult(t *testing.T) {
jsonData := `{
"name": "make",
"age": "22",
"is_use": "1"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
IsUse bool `json:"is_use"`
}
err := ToStruct(jsonData, people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 合法验证:空数据
func TestJsonToStructEmptyValue(t *testing.T) {
jsonData := `{
"name": "make",
"age": "",
"source": "",
"num": "",
"status": ""
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
Source uint `json:"source"`
Num float64 `json:"num"`
Status bool `json:"status"`
}
err := ToStruct(jsonData, &people)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// 非法验证非法的int
func TestJsonToStructErrInt(t *testing.T) {
jsonData := `{
"name": "make",
"age": "test abc"
}`
var people struct {
Name string `json:"name,omitempty"`
Age int `json:"age"`
}
err := ToStruct(jsonData, people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 非法验证非法的uint
func TestJsonToStructErrUint(t *testing.T) {
jsonData := `{
"name": "make",
"age": "test abc"
}`
var people struct {
Name string `json:"name,omitempty"`
Age uint `json:"age"`
}
err := ToStruct(jsonData, people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 非法验证非法的float
func TestJsonToStructErrFloat(t *testing.T) {
jsonData := `{
"name": "make",
"age": "test abc"
}`
var people struct {
Name string `json:"name,omitempty"`
Age float64 `json:"age"`
}
err := ToStruct(jsonData, people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 非法验证:嵌套的数据类型错误
func TestJsonToStructNestErrInt(t *testing.T) {
jsonData := `{
"name": "John Doe",
"address": {
"number": "test abc"
}
}`
type Address struct {
Number int `json:"number"`
}
type Person struct {
Name string `json:"name"`
Address Address `json:"address"`
}
people := &Person{}
err := ToStruct(jsonData, people)
if err == nil {
t.Errorf("err %s", err)
return
}
}
// 合法验证:多层级嵌套
func TestJsonToStructMoreNest(t *testing.T) {
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Source float64 `json:"source"`
Number int `json:"number"`
Status bool `json:"status"`
Address Address `json:"address"`
Emails []string `json:"emails"`
}
jsonData := `{
"name": "John Doe",
"age": 30,
"source": "99.99",
"status": true,
"address": {
"city": "Shanghai",
"country": "China"
},
"emails": [
"john.doe@example.com",
"jdoe@example.com"
]
}`
expectedPerson := Person{
Name: "John Doe",
Age: 30,
Status: true,
Address: Address{
City: "Shanghai",
Country: "China",
},
Emails: []string{"john.doe@example.com", "jdoe@example.com"},
}
var resultPerson Person
err := ToStruct(jsonData, &resultPerson)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if resultPerson.Name != expectedPerson.Name {
t.Errorf("Name field mismatch: expected %s but got %s", expectedPerson.Name, resultPerson.Name)
}
if resultPerson.Age != expectedPerson.Age {
t.Errorf("Age field mismatch: expected %d but got %d", expectedPerson.Age, resultPerson.Age)
}
if resultPerson.Status != expectedPerson.Status {
t.Errorf("Status field mismatch: expected %v but got %v", expectedPerson.Status, resultPerson.Status)
}
if resultPerson.Address.City != expectedPerson.Address.City {
t.Errorf("Address.City field mismatch: expected %s but got %s", expectedPerson.Address.City, resultPerson.Address.City)
}
if resultPerson.Address.Country != expectedPerson.Address.Country {
t.Errorf("Address.Country field mismatch: expected %s but got %s", expectedPerson.Address.Country, resultPerson.Address.Country)
}
if len(resultPerson.Emails) != len(expectedPerson.Emails) {
t.Errorf("Emails length mismatch: expected %d but got %d", len(expectedPerson.Emails), len(resultPerson.Emails))
}
for i, expectedEmail := range expectedPerson.Emails {
if resultPerson.Emails[i] != expectedEmail {
t.Errorf("Emails[%d] mismatch: expected %s but got %s", i, expectedEmail, resultPerson.Emails[i])
}
}
}
// 合法验证:多层级嵌套
func TestJsonToStructMoreNest2(t *testing.T) {
var jsonData = `
{
"uid": 666,
"use_id": ["hello", 5, 9],
"age": "20",
"equip": {
"keyMike": true,
"keyTom": false
},
"happy": {
"k1": [1, 2, 3],
"k2": [4, 5, 6]
},
"slices": [{
"nickname": "ABC",
"money": "20"
}, {
"nickname": "EFG",
"money": "22"
}],
"maps": {
"m1": {
"name": "alis",
"age": "20"
},
"m2": {
"name": "jom",
"age": "22"
}
}
}
`
type SliceVal struct {
Nickname string `json:"nickname"`
Money int `json:"money"`
}
type MapVal struct {
Name string `json:"name"`
Age int `json:"age"`
}
type Target struct {
Uid int `json:"uid"`
UseId []string `json:"use_id"`
Age int `json:"age"`
Equip map[string]bool `json:"equip"`
Happy map[string][]int `json:"happy"`
Slices []SliceVal `json:"slices"`
Maps map[string]MapVal `json:"maps"`
}
var res Target
err := ToStruct(jsonData, &res)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// jsonRes, _ := json.Marshal(res)
// fmt.Printf("%+v \n", res)
// fmt.Println(string(jsonRes))
expected := Target{
Uid: 666,
UseId: []string{"hello", "5", "9"},
Age: 20,
Equip: map[string]bool{
"keyMike": true,
"keyTom": false,
},
Happy: map[string][]int{
"k1": {1, 2, 3},
"k2": {4, 5, 6},
},
Slices: []SliceVal{
{
Nickname: "ABC",
Money: 20,
},
{
Nickname: "EFG",
Money: 22,
},
},
Maps: map[string]MapVal{
"m1": {
Name: "alis",
Age: 20,
},
"m2": {
Name: "jom",
Age: 22,
},
},
}
if !reflect.DeepEqual(res, expected) {
t.Errorf("Result not as expected.\nExpected: %+v\nActual: %+v\n", expected, res)
}
}
// 普通数据类型定义 interface
func TestJsonToStructAny1(t *testing.T) {
jsonData := `{
"name": "make",
"age": "10"
}`
type Target struct {
Name interface{} `json:"name"`
Age uint `json:"age"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// 普通数据类型定义 any
func TestJsonToStructAny2(t *testing.T) {
jsonData := `{
"name": "make",
"age": "10"
}`
type Target struct {
Name any `json:"name"`
Age uint `json:"age"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// slice 中包含 interface
func TestJsonToStructAny3(t *testing.T) {
jsonData := `{
"name": ["make",6,"tom"]
}`
type Target struct {
Name []interface{} `json:"name"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// slice 中包含 any
func TestJsonToStructAny4(t *testing.T) {
jsonData := `{
"name": ["make",6,"tom"]
}`
type Target struct {
Name []any `json:"name"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
fmt.Println(target, err)
}
// map 中包含 interface
func TestJsonToStructAny5(t *testing.T) {
jsonData := `{
"name": {
"key1": "mike",
"key2": 666
}
}`
type Target struct {
Name map[string]interface{} `json:"name"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// map 中包含 any
func TestJsonToStructAny6(t *testing.T) {
jsonData := `{
"name": {
"key1": "mike",
"key2": 666
}
}`
type Target struct {
Name map[string]any `json:"name"`
}
var target Target
err := ToStruct(jsonData, &target)
if err != nil {
t.Errorf("err %s", err)
return
}
}
// 多层级json测试
// func TestJsonToStruct4(t *testing.T) {
// type Address struct {
// City string `json:"city"`
// Street string `json:"street"`
// Zipcode uint64 `json:"zipcode"`
// }
//
// type Score struct {
// Subject string `json:"subject"`
// Score int `json:"score"`
// }
//
// type Student struct {
// Name string `json:"name,omitempty"`
// Age int `json:"age,omitempty"`
// Address Address `json:"address"`
// Scores []Score `json:"scores,omitempty"`
// }
// jsonStr4 := `{
// "name": "Alice",
// "age": 30,
// "address": {
// "city": "Beijing",
// "street": "Zhangsan Street",
// "zipcode": 100
// },
// "scores": [
// {"subject": "Math", "score": 80},
// {"subject": "English", "score": 90}
// ]
// }`
//
// var student Student
// if err := JsonToStruct(jsonStr4, &student); err != nil {
// fmt.Println(err)
// return
// }
//
// fmt.Printf("%+v \n", student)
// }
// func TestJsonToStruct5(t *testing.T) {
// type Student struct {
// Name string `json:"name,omitempty"`
// Age int `json:"age,omitempty"`
// }
// jsonStr4 := `{
// "name":null,
// "age": "30"
// }`
//
// var student Student
// if err := JsonToStruct(jsonStr4, &student); err != nil {
// fmt.Println(err)
// return
// }
//
// fmt.Printf("%+v \n", student)
// }
// func TestJsonToStruct6(t *testing.T) {
// type Student struct {
// Name any `json:"name,omitempty"`
// Age int `json:"age,omitempty"`
// }
// jsonStr4 := `{
// "name":"zhangsan",
// "age": "123"
// }`
//
// var student Student
// if err := JsonToStruct(jsonStr4, &student); err != nil {
// fmt.Println(err)
// return
// }
//
// fmt.Printf("%+v \n", student)
// }
// func TestJsonToStruct7(t *testing.T) {
// type Student struct {
// Name bool `json:"name"`
// Name2 uint `json:"name2"`
// Name3 uint `json:"name3"`
// Age int `json:"age"`
// }
// jsonStr4 := `{
// "name": true,
// "name2": -1,
// "name3": null,
// "age": "123"
// }`
//
// var student Student
// if err := JsonToStruct(jsonStr4, &student); err != nil {
// fmt.Println(err)
// return
// }
//
// fmt.Printf("%#v \n", student)
// }

View File

@@ -1,60 +0,0 @@
package jsonx
import (
"reflect"
"strconv"
)
func toUint64Reflect(i any) (uint64, error) {
if i == nil {
return 0, nil
}
v := reflect.ValueOf(i)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return 0, nil
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint(), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intValue := v.Int()
if intValue < 0 {
return 0, nil
}
return uint64(intValue), nil
case reflect.Float32, reflect.Float64:
floatValue := v.Float()
if floatValue < 0 {
return 0, nil
}
return uint64(floatValue), nil
case reflect.Complex64, reflect.Complex128:
realValue := real(v.Complex())
if realValue < 0 {
return 0, nil
}
return uint64(realValue), nil
case reflect.String:
if v.String() == "" {
return 0, nil
}
uintValue, err := strconv.ParseUint(v.String(), 10, 64)
if err != nil {
return 0, ErrSyntax
}
return uintValue, nil
case reflect.Bool:
if v.Bool() {
return 1, nil
} else {
return 0, nil
}
default:
return 0, ErrType
}
}

View File

@@ -1,171 +0,0 @@
package jsonx
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestToUint64(t *testing.T) {
var iPtr = 90
tests := []struct {
name string
input any
want uint64
wantError error
}{
{
name: "Test -float32",
input: float32(-0.1),
want: 0,
},
{
name: "Test -float64",
input: float64(-0.2),
want: 0,
},
{
name: "Test -int",
input: int(-1),
want: 0,
},
{
name: "Test -int8",
input: int8(-2),
want: 0,
},
{
name: "Test -int16",
input: int16(-3),
want: 0,
},
{
name: "Test -int32",
input: int32(-4),
want: 0,
},
{
name: "Test -int64",
input: int64(-5),
want: 0,
},
{
name: "Test uint",
input: uint(12),
want: 12,
},
{
name: "Test uint8",
input: uint8(42),
want: 42,
},
{
name: "Test uint16",
input: uint16(42),
want: 42,
},
{
name: "Test uint32",
input: uint32(42),
want: 42,
},
{
name: "Test uint64",
input: uint64(42),
want: 42,
},
{
name: "Test int8",
input: int8(42),
want: 42,
},
{
name: "Test int16",
input: int16(42),
want: 42,
},
{
name: "Test int32",
input: int32(42),
want: 42,
},
{
name: "Test int64",
input: int64(42),
want: 42,
},
{
name: "Test float32",
input: float32(42.0),
want: 42,
},
{
name: "Test float64",
input: float64(42.0),
want: 42,
},
{
name: "Test complex64",
input: complex64(complex(42, 0)),
want: 42,
},
{
name: "Test complex128",
input: complex128(complex(42, 0)),
want: 42,
},
{
name: "test -complex",
input: complex(-1, -1),
want: 0,
},
{
name: "Test string",
input: "42",
want: 42,
},
{
name: "Test invalid string",
input: "not a number",
wantError: ErrSyntax,
},
{
name: "Test nil pointer",
input: (*int)(nil),
want: 0,
wantError: nil,
},
{
name: "Test bool true",
input: true,
want: 1,
},
{
name: "Test bool false",
input: false,
want: 0,
},
{
name: "Test invalid type",
input: make(chan int),
wantError: ErrType,
},
{
name: "Test point",
input: &iPtr,
want: 90,
},
{
name: "nil",
input: nil,
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := toUint64Reflect(tt.input)
assert.Equal(t, tt.wantError, err)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,19 +0,0 @@
package jsonx
import (
"errors"
"strconv"
)
var (
// ErrPoint 不是指针类型
ErrPoint = errors.New("the argument to Result must be a non-nil pointer")
// ErrNotMap 不是Map类型
ErrNotMap = errors.New("cannot parse map, value is not a map")
// ErrNotSlice 不是Slice类型
ErrNotSlice = errors.New("cannot parse slice, value is not a slice")
// ErrSyntax 指示值不具有目标类型的正确语法
ErrSyntax = strconv.ErrSyntax
// ErrType 指示值不具有目标类型的正确语法
ErrType = errors.New("unsupported type")
)