chore(cast): bump version to v1.0.4 and rename JSON/YAML functions

This commit is contained in:
AI Engineer 2026-05-01 00:41:23 +08:00
parent 211ed95856
commit 72e6f54df0
5 changed files with 101 additions and 108 deletions

View File

@ -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`

View File

@ -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 查重优化。

View File

@ -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)
}
}

133
cast.go
View File

@ -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)
}

View File

@ -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 会变成 "<a> \u0026 <b>"
// 我们期望输出原始字符 "<a> & <b>"
js := cast.MustToJson(c)
js := cast.MustToJSON(c)
expected := `{"text":"<a> & <b>"}`
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)
}
}