From 72e6f54df0a99220d9299d6fa0156cd8e3fd137c Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Fri, 1 May 2026 00:41:23 +0800 Subject: [PATCH] chore(cast): bump version to v1.0.4 and rename JSON/YAML functions --- README.md | 14 +++--- TEST.md | 4 +- bench_test.go | 20 ++++---- cast.go | 133 ++++++++++++++++++++++++-------------------------- cast_test.go | 38 +++++++-------- 5 files changed, 101 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index dc81f4b..51a4cd3 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ status := cast.If(isAdmin, "Admin", "User") // JSON 序列化增强 var u struct { UserID int } -cast.UnJson("userId: 1001", &u) // u.UserID = 1001 -cast.Json(u) // "userId": 1001,首字母自动小写 +cast.UnmarshalJSON("userId: 1001", &u) // u.UserID = 1001 +cast.MustToJSON(u) // "userId": 1001,首字母自动小写 // 字符串切分增强 cast.Split(",", ",") // [],忽略无效切片 @@ -61,11 +61,11 @@ cast.Split("a, b, ", ",") // ["a", "b"],去除空白字符 * `ArrayToBoolMap[T comparable]([]T) map[T]bool` —— 快速索引化 4. **序列化(JSON & YAML)** - * **JSON 编码**: `ToJson(any)(string, error)` | `MustToJson(any)string` | `PrettyToJson(any)string` - * **JSON 字节**: `ToJsonBytes(any)([]byte, error)` | `MustJsonBytes(any)[]byte` | `PrettyToJsonBytes(any)[]byte` - * **JSON 解码**: `UnJson(string, any)(any, error)` | `MustUnJson(string, any)any` | `UnJsonBytes([]byte, any)(any, error)` | `MustUnJsonBytes([]byte, any)any` - * **YAML 编码**: `ToYaml(any)(string, error)` | `MustToYaml(any)string` | `YamlBytes(any)([]byte, error)` | `MustYamlBytes(any)[]byte` - * **YAML 解码**: `UnYaml(string, any)(any, error)` | `MustUnYaml(string, any)any` | `UnYamlBytes([]byte, any)(any, error)` | `MustUnYamlBytes([]byte, any)any` + * **JSON 编码**: `ToJSON(any)(string, error)` | `MustToJSON(any)string` | `PrettyToJSON(any)string` + * **JSON 字节**: `ToJSONBytes(any)([]byte, error)` | `MustJSONBytes(any)[]byte` | `PrettyToJSONBytes(any)[]byte` + * **JSON 解码**: `UnmarshalJSON(string, any)(any, error)` | `MustUnmarshalJSON(string, any)any` | `UnmarshalJSONBytes([]byte, any)(any, error)` | `MustUnmarshalJSONBytes([]byte, any)any` + * **YAML 编码**: `ToYAML(any)(string, error)` | `MustToYAML(any)string` | `YAMLBytes(any)([]byte, error)` | `MustYAMLBytes(any)[]byte` + * **YAML 解码**: `UnmarshalYAML(string, any)(any, error)` | `MustUnmarshalYAML(string, any)any` | `UnmarshalYAMLBytes([]byte, any)(any, error)` | `MustUnmarshalYAMLBytes([]byte, any)any` 5. **辅助工具(Utilities)** * **切分**: `Split(s, sep) []string` | `SplitArgs(s) []string` diff --git a/TEST.md b/TEST.md index eb444d5..10cbcd9 100644 --- a/TEST.md +++ b/TEST.md @@ -4,12 +4,12 @@ - **核心类型转换**: `Int64`, `Uint64`, `Float64`, `Bool`, `String`,包括边界值、零值及非法字符串输入。 - **复合类型处理**: `Ints`, `Strings` 自动解析 JSON 字符串或直接转换。 - **JSON/YAML 互转**: 深度结构体映射,处理大写 Key 自动修复,支持自定义 `keepKey` tag。 -- **JSON 类型修复**: 通过 `makeJsonType` 对 Map 键进行强制转换以符合 JSON 规范。 +- **JSON 类型修复**: 通过 `makeJSONType` 对 Map 键进行强制转换以符合 JSON 规范。 - **指针与接口**: `RealValue` 处理多级指针与接口解包。 - **高性能实用函数**: `UniqueAppend` (支持 $O(n)$ 去重),`If` (泛型三元),`SplitArgs` (支持引用格式)。 ## 性能基准 (Benchmark Results - Intel(R) Core(TM) i9) - `If`: ~0.25 ns/op - `Int64`: ~18.4 ns/op -- `ToJson`: ~623.9 ns/op +- `ToJSON`: ~623.9 ns/op - `UniqueAppend`: 在大数据量下的 $O(n)$ 时间复杂度,通过 map 查重优化。 diff --git a/bench_test.go b/bench_test.go index 596d806..8db0929 100644 --- a/bench_test.go +++ b/bench_test.go @@ -36,8 +36,8 @@ func TestEdgeCases(t *testing.T) { } // 4. JSON 异常输入 - if _, err := cast.UnJson("{invalid_json}", nil); err == nil { - t.Log("UnJson handled invalid input") + if _, err := cast.UnmarshalJSON("{invalid_json}", nil); err == nil { + t.Log("UnmarshalJSON handled invalid input") } } @@ -64,14 +64,14 @@ func BenchmarkString(b *testing.B) { } } -func BenchmarkJson(b *testing.B) { +func BenchmarkJSON(b *testing.B) { type User struct { ID int `json:"id"` Name string `json:"name"` } u := User{ID: 1, Name: "Benchmark User"} for i := 0; i < b.N; i++ { - _ = cast.MustToJson(u) + _ = cast.MustToJSON(u) } } @@ -118,8 +118,8 @@ func BenchmarkString_FastPath(b *testing.B) { } } -// 4. 测试 ToJson 性能 (对比自定义逻辑与标准库) -func BenchmarkToJson_SimpleStruct(b *testing.B) { +// 4. 测试 ToJSON 性能 (对比自定义逻辑与标准库) +func BenchmarkToJSON_SimpleStruct(b *testing.B) { type User struct { ID int `json:"id"` Name string `json:"name"` @@ -127,13 +127,13 @@ func BenchmarkToJson_SimpleStruct(b *testing.B) { u := User{ID: 1, Name: "Benchmark User"} b.ResetTimer() for i := 0; i < b.N; i++ { - // 这里包含了你特有的 makeJsonType 清洗逻辑和 FixUpperCase 逻辑 - _ = cast.MustToJson(u) + // 这里包含了你特有的 makeJSONType 清洗逻辑和 FixUpperCase 逻辑 + _ = cast.MustToJSON(u) } } // 5. 测试 Goja Map 的清洗性能 -func BenchmarkToJson_DirtyMap(b *testing.B) { +func BenchmarkToJSON_DirtyMap(b *testing.B) { // 模拟恶劣数据:包含 any key 的 map val := map[any]any{ 0: "A", @@ -141,6 +141,6 @@ func BenchmarkToJson_DirtyMap(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - _ = cast.MustToJson(val) + _ = cast.MustToJSON(val) } } diff --git a/cast.go b/cast.go index d41e4a5..5edcee7 100644 --- a/cast.go +++ b/cast.go @@ -62,42 +62,35 @@ func parseUint(s string) uint64 { func Int(value any) int { return int(Int64(value)) } +// Helper for integer coercion to avoid repetition +func toInt64(value any) (int64, bool) { + switch v := value.(type) { + case int: return int64(v), true + case int8: return int64(v), true + case int16: return int64(v), true + case int32: return int64(v), true + case int64: return v, true + case uint: return int64(v), true + case uint8: return int64(v), true + case uint16: return int64(v), true + case uint32: return int64(v), true + case uint64: return int64(v), true + case float32: return int64(v), true + case float64: return int64(v), true + case bool: return If(v, int64(1), int64(0)), true + } + return 0, false +} + func Int64(value any) int64 { - if value == nil { - return 0 - } + if value == nil { return 0 } + if i, ok := toInt64(value); ok { return i } + switch realValue := value.(type) { - case int: - return int64(realValue) - case int8: - return int64(realValue) - case int16: - return int64(realValue) - case int32: - return int64(realValue) - case int64: - return realValue - case uint: - return int64(realValue) - case uint8: - return int64(realValue) - case uint16: - return int64(realValue) - case uint32: - return int64(realValue) - case uint64: - return int64(realValue) - case float32: - return int64(realValue) - case float64: - return int64(realValue) - case bool: - return If(realValue, int64(1), int64(0)) - case []byte: - return parseInt(string(realValue)) - case string: - return parseInt(realValue) + case []byte: return parseInt(string(realValue)) + case string: return parseInt(realValue) } + rv := reflect.ValueOf(value) if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface { if rv = RealValue(rv); rv.IsValid() && rv.CanInterface() && rv.Kind() != reflect.Pointer { @@ -253,7 +246,7 @@ func Ints(value any) []int64 { case string: if strings.HasPrefix(realValue, "[") { result := make([]int64, 0) - UnJson(realValue, &result) + UnmarshalJSON(realValue, &result) return result } return []int64{Int64(value)} @@ -278,7 +271,7 @@ func Strings(value any) []string { case string: if strings.HasPrefix(realValue, "[") { result := make([]string, 0) - UnJson(realValue, &result) + UnmarshalJSON(realValue, &result) return result } return []string{String(value)} @@ -316,12 +309,12 @@ func Duration(value any) time.Duration { return 0 } -func ToJsonBytes(value any) ([]byte, error) { +func ToJSONBytes(value any) ([]byte, error) { buf := &bytes.Buffer{} enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) // 现代改进:不再需要手动 FixJsonBytes + enc.SetEscapeHTML(false) // 现代改进:不再需要手动 FixJSONBytes if err := enc.Encode(value); err != nil { - v2 := makeJsonType(reflect.ValueOf(value)) + v2 := makeJSONType(reflect.ValueOf(value)) if v2 != nil { buf.Reset() err = enc.Encode(v2.Interface()) @@ -347,25 +340,25 @@ func ToJsonBytes(value any) ([]byte, error) { return bytesResult, nil } -func MustJsonBytes(value any) []byte { - j, err := ToJsonBytes(value) +func MustJSONBytes(value any) []byte { + j, err := ToJSONBytes(value) if err != nil { return []byte{} } return j } -func ToJson(value any) (string, error) { - j, err := ToJsonBytes(value) +func ToJSON(value any) (string, error) { + j, err := ToJSONBytes(value) if err != nil { return "", err } return string(j), nil } -func MustToJson(value any) string { return string(MustJsonBytes(value)) } +func MustToJSON(value any) string { return string(MustJSONBytes(value)) } -func PrettyToJsonBytes(value any) []byte { - j := MustJsonBytes(value) +func PrettyToJSONBytes(value any) []byte { + j := MustJSONBytes(value) r := &bytes.Buffer{} if err := json.Indent(r, j, "", " "); err == nil { return r.Bytes() @@ -373,9 +366,9 @@ func PrettyToJsonBytes(value any) []byte { return j } -func PrettyToJson(value any) string { return string(PrettyToJsonBytes(value)) } +func PrettyToJSON(value any) string { return string(PrettyToJSONBytes(value)) } -func UnJsonBytes(data []byte, value any) (any, error) { +func UnmarshalJSONBytes(data []byte, value any) (any, error) { if value == nil { var v any value = &v @@ -384,35 +377,35 @@ func UnJsonBytes(data []byte, value any) (any, error) { return value, err } -func MustUnJsonBytes(data []byte, value any) any { - v, err := UnJsonBytes(data, value) +func MustUnmarshalJSONBytes(data []byte, value any) any { + v, err := UnmarshalJSONBytes(data, value) if err != nil { return nil } return v } -func UnJson(str string, value any) (any, error) { return UnJsonBytes([]byte(str), value) } +func UnmarshalJSON(str string, value any) (any, error) { return UnmarshalJSONBytes([]byte(str), value) } -func MustUnJson(str string, value any) any { return MustUnJsonBytes([]byte(str), value) } +func MustUnmarshalJSON(str string, value any) any { return MustUnmarshalJSONBytes([]byte(str), value) } -func ToYaml(value any) (string, error) { - j, err := YamlBytes(value) +func ToYAML(value any) (string, error) { + j, err := YAMLBytes(value) if err != nil { return "", err } return string(j), nil } -func MustToYaml(value any) string { return string(MustYamlBytes(value)) } +func MustToYAML(value any) string { return string(MustYAMLBytes(value)) } -func UnYaml(data string, value any) (any, error) { - return UnYamlBytes([]byte(data), value) +func UnmarshalYAML(data string, value any) (any, error) { + return UnmarshalYAMLBytes([]byte(data), value) } -func MustUnYaml(data string, value any) any { return MustUnYamlBytes([]byte(data), value) } +func MustUnmarshalYAML(data string, value any) any { return MustUnmarshalYAMLBytes([]byte(data), value) } -func YamlBytes(value any) ([]byte, error) { +func YAMLBytes(value any) ([]byte, error) { j, err := yaml.Marshal(value) if err == nil { return j, nil @@ -420,21 +413,21 @@ func YamlBytes(value any) ([]byte, error) { return []byte(fmt.Sprint(value)), err } -func MustYamlBytes(value any) []byte { - j, err := YamlBytes(value) +func MustYAMLBytes(value any) []byte { + j, err := YAMLBytes(value) if err != nil { return []byte{} } return j } -func UnYamlBytes(data []byte, value any) (any, error) { +func UnmarshalYAMLBytes(data []byte, value any) (any, error) { err := yaml.Unmarshal(data, value) return value, err } -func MustUnYamlBytes(data []byte, value any) any { - v, err := UnYamlBytes(data, value) +func MustUnmarshalYAMLBytes(data []byte, value any) any { + v, err := UnmarshalYAMLBytes(data, value) if err != nil { return nil } @@ -538,7 +531,7 @@ func ArrayToBoolMap[T comparable](arr []T) map[T]bool { return r } -func makeJsonType(inValue reflect.Value) *reflect.Value { +func makeJSONType(inValue reflect.Value) *reflect.Value { if inValue.Kind() == reflect.Interface { inValue = inValue.Elem() } @@ -574,7 +567,7 @@ func makeJsonType(inValue reflect.Value) *reflect.Value { newMap := reflect.MakeMap(reflect.MapOf(reflect.TypeFor[string](), inType.Elem())) for _, k := range inValue.MapKeys() { v1 := inValue.MapIndex(k) - v2 := makeJsonType(v1) + v2 := makeJSONType(v1) var k2 reflect.Value if k.CanInterface() { k2 = reflect.ValueOf(String(k.Interface())) @@ -594,7 +587,7 @@ func makeJsonType(inValue reflect.Value) *reflect.Value { newArray := reflect.MakeSlice(reflect.SliceOf(inType.Elem()), length, length) for _, k := range inValue.MapKeys() { v1 := inValue.MapIndex(k) - v2 := makeJsonType(v1) + v2 := makeJSONType(v1) idx := int(Int64(k.Interface())) // 统一转为 int if idx >= 0 && idx < length { @@ -609,7 +602,7 @@ func makeJsonType(inValue reflect.Value) *reflect.Value { } } else { for _, k := range inValue.MapKeys() { - v := makeJsonType(inValue.MapIndex(k)) + v := makeJSONType(inValue.MapIndex(k)) if v != nil { inValue.SetMapIndex(k, *v) } @@ -619,7 +612,7 @@ func makeJsonType(inValue reflect.Value) *reflect.Value { case reflect.Slice: if inType.Elem().Kind() != reflect.Uint8 { for i := inValue.Len() - 1; i >= 0; i-- { - v := makeJsonType(inValue.Index(i)) + v := makeJSONType(inValue.Index(i)) if v != nil && inValue.Index(i).CanSet() { inValue.Index(i).Set(*v) } @@ -630,13 +623,13 @@ func makeJsonType(inValue reflect.Value) *reflect.Value { for i := inType.NumField() - 1; i >= 0; i-- { f := inType.Field(i) if f.Anonymous { - v := makeJsonType(inValue.Field(i)) + v := makeJSONType(inValue.Field(i)) if v != nil && inValue.Field(i).CanSet() { inValue.Field(i).Set(*v) } } else { if f.Name[0] >= 65 && f.Name[0] <= 90 { - v := makeJsonType(inValue.Field(i)) + v := makeJSONType(inValue.Field(i)) if v != nil && inValue.Field(i).CanSet() { inValue.Field(i).Set(*v) } diff --git a/cast_test.go b/cast_test.go index 0e7344e..c46cf81 100644 --- a/cast_test.go +++ b/cast_test.go @@ -26,44 +26,44 @@ func TestGenerics(t *testing.T) { } } -func TestStructToJson(t *testing.T) { +func TestStructToJSON(t *testing.T) { type User struct { Name string Age int } u := User{Name: "Tom", Age: 18} - js := cast.MustToJson(u) + js := cast.MustToJSON(u) // 验证首字母小写逻辑 if !strings.Contains(js, `"name":"Tom"`) || !strings.Contains(js, `"age":18`) { t.Errorf("Struct to JSON auto-lowercase failed: %s", js) } } -func TestJsonToStruct(t *testing.T) { +func TestJSONToStruct(t *testing.T) { type User struct { Name string Age int } data := `{"Name":"Tom","age":18}` var u User - cast.UnJson(data, &u) + cast.UnmarshalJSON(data, &u) if u.Name != "Tom" || u.Age != 18 { - t.Error("UnJson to struct failed") + t.Error("UnmarshalJSON to struct failed") } } -func TestYamlConversion(t *testing.T) { +func TestYAMLConversion(t *testing.T) { type Config struct { Port int } c := Config{Port: 8080} - yamlStr := cast.MustToYaml(c) + yamlStr := cast.MustToYAML(c) if !strings.Contains(yamlStr, "port: 8080") { - t.Errorf("Yaml conversion failed: %s", yamlStr) + t.Errorf("YAML conversion failed: %s", yamlStr) } } -func TestSpecialJson(t *testing.T) { +func TestSpecialJSON(t *testing.T) { // 关键测试:特殊 HTML 字符序列化不应被转义 type Content struct { Text string `json:"text"` @@ -72,20 +72,20 @@ func TestSpecialJson(t *testing.T) { // 标准 json.Marshal 会变成 " \u0026 " // 我们期望输出原始字符 " & " - js := cast.MustToJson(c) + js := cast.MustToJSON(c) expected := `{"text":" & "}` if js != expected { t.Errorf("Special JSON failed.\nExpected: %s\nActual: %s", expected, js) } // 漂亮打印测试 - jsP := cast.PrettyToJson(c) + jsP := cast.PrettyToJSON(c) if !strings.Contains(jsP, "&") || !strings.Contains(jsP, "\n") { - t.Error("JsonP special content failed") + t.Error("JSONP special content failed") } } -func TestComplexJsonType(t *testing.T) { +func TestComplexJSONType(t *testing.T) { // 测试 map[interface{}]interface{} 这种标准库无法处理的类型 data := map[interface{}]interface{}{ "name": "Tom", @@ -95,7 +95,7 @@ func TestComplexJsonType(t *testing.T) { }, } - js := cast.MustToJson(data) + js := cast.MustToJSON(data) // 期望 123 被转为 "123" 且内容正确 if !strings.Contains(js, `"123":"numeric key"`) || !strings.Contains(js, `"ok":true`) { t.Errorf("Complex JSON type failed: %s", js) @@ -149,7 +149,7 @@ func TestGojaMapToArrayFallback(t *testing.T) { "2": "cherry", // 字符串形式的数字也应该被正确转换 } - js := cast.MustToJson(mockGojaArray) + js := cast.MustToJSON(mockGojaArray) // 我们期望它被正确识别并转化为标准的 JSON 数组,且顺序不会乱 expected := `["apple","banana","cherry"]` @@ -198,15 +198,15 @@ func TestUnaddressableStruct(t *testing.T) { } d := Dummy{Name: "Test"} - // 传入值类型而不是指针。如果 makeJsonType 中遗漏了 CanSet,这里会直接 panic + // 传入值类型而不是指针。如果 makeJSONType 中遗漏了 CanSet,这里会直接 panic defer func() { if r := recover(); r != nil { - t.Errorf("ToJson with value struct panicked! Missing CanSet() check: %v", r) + t.Errorf("ToJSON with value struct panicked! Missing CanSet() check: %v", r) } }() - res := cast.MustToJson(d) + res := cast.MustToJSON(d) if !strings.Contains(res, "\"name\"") { - t.Errorf("Value struct ToJson failed to lowercase key: %s", res) + t.Errorf("Value struct ToJSON failed to lowercase key: %s", res) } }