feat: 添加 fieldx 包,支持基于 Schema 的对象生成
新增 fieldx 包,提供基于预定义 Schema 生成 map[string]any 对象的功能。 主要特性: - 支持固定值字段 (string)、字段引用 (field) 和嵌套对象 (object) - 支持点号分隔的嵌套路径访问 (如 "user.name") - 提供多种 Schema 创建方式 (JSON、Map、编程方式) - 完善的错误处理和文档示例
This commit is contained in:
parent
d233fc319b
commit
a6d3e34e53
292
fieldx/README.md
Normal file
292
fieldx/README.md
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
# FieldX - Schema-based Object Generator
|
||||||
|
|
||||||
|
FieldX 提供了基于预定义 Schema 生成 `map[string]any` 对象的功能。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- **固定值字段 (string)**: 在生成的对象中设置固定的字符串值
|
||||||
|
- **字段引用 (field)**: 从源对象中获取指定字段的值,支持嵌套路径访问
|
||||||
|
- **嵌套对象 (object)**: 支持生成多层嵌套的复杂对象结构
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "git.fsdpf.net/go/reflux/fieldx"
|
||||||
|
|
||||||
|
// 1. 定义 JSON Schema
|
||||||
|
jsonSchema := `{
|
||||||
|
"name": {"type": "field", "value": "userName"},
|
||||||
|
"status": {"type": "string", "value": "active"}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// 2. 从 JSON 创建 Schema
|
||||||
|
schema, _ := fieldx.SchemaFromJSON(jsonSchema)
|
||||||
|
|
||||||
|
// 3. 直接生成对象 (一行代码!)
|
||||||
|
source := map[string]any{"userName": "Alice"}
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// result: map[name:Alice status:active]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 推荐方式: 直接使用 Schema
|
||||||
|
|
||||||
|
最简单直接的方式:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "git.fsdpf.net/go/reflux/fieldx"
|
||||||
|
|
||||||
|
// 1. 从 JSON 创建 Schema
|
||||||
|
schema, _ := fieldx.SchemaFromJSON(`{
|
||||||
|
"name": {"type": "field", "value": "userName"},
|
||||||
|
"status": {"type": "string", "value": "active"}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
// 2. 直接生成对象
|
||||||
|
source := map[string]any{"userName": "Alice"}
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
// result: map[name:Alice status:active]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 创建 Schema 的方式
|
||||||
|
|
||||||
|
#### 方式一: SchemaFromJSON (推荐)
|
||||||
|
```go
|
||||||
|
schema, _ := fieldx.SchemaFromJSON(jsonStr)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方式二: json.Unmarshal
|
||||||
|
```go
|
||||||
|
var schema fieldx.Schema
|
||||||
|
json.Unmarshal([]byte(jsonStr), &schema)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方式三: SchemaFromMap
|
||||||
|
```go
|
||||||
|
var data map[string]any
|
||||||
|
json.Unmarshal([]byte(jsonStr), &data)
|
||||||
|
schema, _ := fieldx.SchemaFromMap(data)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方式四: 编程方式
|
||||||
|
```go
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
"field": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "value",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 嵌套对象示例
|
||||||
|
|
||||||
|
```go
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
"type": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "user_profile",
|
||||||
|
},
|
||||||
|
"user": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeObject,
|
||||||
|
Fields: fieldx.Schema{
|
||||||
|
"id": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "userId",
|
||||||
|
},
|
||||||
|
"name": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "userName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
source := map[string]any{
|
||||||
|
"userId": 123,
|
||||||
|
"userName": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
// result: {"type": "user_profile", "user": {"id": 123, "name": "Alice"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 嵌套路径访问
|
||||||
|
|
||||||
|
支持使用点号分隔的路径从嵌套的源对象中获取值:
|
||||||
|
|
||||||
|
```go
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
"userId": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.id", // 使用点号访问嵌套字段
|
||||||
|
},
|
||||||
|
"userEmail": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.contact.email", // 支持多层嵌套
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 456,
|
||||||
|
"contact": map[string]any{
|
||||||
|
"email": "alice@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
// result: {"userId": 456, "userEmail": "alice@example.com"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 字段类型说明
|
||||||
|
|
||||||
|
### FieldTypeString
|
||||||
|
固定字符串值类型。
|
||||||
|
|
||||||
|
```go
|
||||||
|
fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "固定的字符串值",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### FieldTypeField
|
||||||
|
从源对象获取字段值,支持点号分隔的嵌套路径。
|
||||||
|
|
||||||
|
```go
|
||||||
|
fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.profile.name", // 支持嵌套路径
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### FieldTypeObject
|
||||||
|
嵌套对象类型,使用 `Fields` 字段定义子结构。
|
||||||
|
|
||||||
|
```go
|
||||||
|
fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeObject,
|
||||||
|
Fields: fieldx.Schema{
|
||||||
|
// 嵌套的字段定义
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON Schema 格式
|
||||||
|
|
||||||
|
Schema 可以从 JSON 反序列化,这是最方便的使用方式:
|
||||||
|
|
||||||
|
### 基本格式
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"字段名": {
|
||||||
|
"type": "string|field|object",
|
||||||
|
"value": "根据type不同而不同",
|
||||||
|
"fields": {/* 当type为object时使用 */}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 类型说明
|
||||||
|
|
||||||
|
**1. string** - 固定字符串值
|
||||||
|
```json
|
||||||
|
{"type": "string", "value": "固定值"}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. field** - 从源对象获取字段(支持点号分隔的嵌套路径)
|
||||||
|
```json
|
||||||
|
{"type": "field", "value": "user.name"}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. object** - 嵌套对象
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"子字段名": {"type": "...", "value": "..."}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 完整 JSON Schema 示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "user_profile"
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.id"
|
||||||
|
},
|
||||||
|
"userName": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.name"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"email": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.email"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "N/A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用此 Schema:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 1. 从 JSON 创建 Schema
|
||||||
|
schema, _ := fieldx.SchemaFromJSON(jsonSchema)
|
||||||
|
|
||||||
|
// 2. 准备源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 123,
|
||||||
|
"name": "Alice",
|
||||||
|
"email": "alice@example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 结果:
|
||||||
|
// {
|
||||||
|
// "type": "user_profile",
|
||||||
|
// "userId": 123,
|
||||||
|
// "userName": "Alice",
|
||||||
|
// "contact": {
|
||||||
|
// "email": "alice@example.com",
|
||||||
|
// "phone": "N/A"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
当引用的字段不存在时,`Generate()` 方法会返回错误:
|
||||||
|
|
||||||
|
```go
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
// 处理错误,例如:
|
||||||
|
// "failed to generate field xxx: field not found: yyy"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整示例
|
||||||
|
|
||||||
|
查看 [example_test.go](example_test.go) 获取更多使用示例。
|
||||||
296
fieldx/example_test.go
Normal file
296
fieldx/example_test.go
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
package fieldx_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.fsdpf.net/go/reflux/fieldx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExampleSchemaFromJSON 演示从 JSON 字符串创建 Schema 并生成对象
|
||||||
|
func ExampleSchemaFromJSON() {
|
||||||
|
// JSON Schema 定义
|
||||||
|
jsonSchema := `{
|
||||||
|
"name": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "active"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"email": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "email"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// 从 JSON 创建 Schema
|
||||||
|
schema, _ := fieldx.SchemaFromJSON(jsonSchema)
|
||||||
|
|
||||||
|
// 源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"userName": "Alice",
|
||||||
|
"email": "alice@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("name: %s\n", result["name"])
|
||||||
|
fmt.Printf("status: %s\n", result["status"])
|
||||||
|
profile := result["profile"].(map[string]any)
|
||||||
|
fmt.Printf("profile.email: %s\n", profile["email"])
|
||||||
|
fmt.Printf("profile.role: %s\n", profile["role"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// name: Alice
|
||||||
|
// status: active
|
||||||
|
// profile.email: alice@example.com
|
||||||
|
// profile.role: user
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSchema_Generate_nestedPath 演示使用嵌套路径访问源数据
|
||||||
|
func ExampleSchema_Generate_nestedPath() {
|
||||||
|
// 定义 Schema(使用点号分隔的路径)
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
"userId": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.id",
|
||||||
|
},
|
||||||
|
"userName": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.name",
|
||||||
|
},
|
||||||
|
"userEmail": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "user.contact.email",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 嵌套的源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 123,
|
||||||
|
"name": "Alice",
|
||||||
|
"contact": map[string]any{
|
||||||
|
"email": "alice@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("userId: %v\n", result["userId"])
|
||||||
|
fmt.Printf("userName: %s\n", result["userName"])
|
||||||
|
fmt.Printf("userEmail: %s\n", result["userEmail"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// userId: 123
|
||||||
|
// userName: Alice
|
||||||
|
// userEmail: alice@example.com
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSchema_Generate_complexNested 演示复杂的嵌套对象生成
|
||||||
|
func ExampleSchema_Generate_complexNested() {
|
||||||
|
// 定义复杂的嵌套 Schema
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
"type": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "user_profile",
|
||||||
|
},
|
||||||
|
"user": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeObject,
|
||||||
|
Fields: fieldx.Schema{
|
||||||
|
"id": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "userId",
|
||||||
|
},
|
||||||
|
"name": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "userName",
|
||||||
|
},
|
||||||
|
"contact": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeObject,
|
||||||
|
Fields: fieldx.Schema{
|
||||||
|
"email": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "email",
|
||||||
|
},
|
||||||
|
"phone": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "N/A",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"userId": 456,
|
||||||
|
"userName": "Bob",
|
||||||
|
"email": "bob@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("type: %s\n", result["type"])
|
||||||
|
user := result["user"].(map[string]any)
|
||||||
|
fmt.Printf("user.id: %v\n", user["id"])
|
||||||
|
fmt.Printf("user.name: %s\n", user["name"])
|
||||||
|
contact := user["contact"].(map[string]any)
|
||||||
|
fmt.Printf("user.contact.email: %s\n", contact["email"])
|
||||||
|
fmt.Printf("user.contact.phone: %s\n", contact["phone"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// type: user_profile
|
||||||
|
// user.id: 456
|
||||||
|
// user.name: Bob
|
||||||
|
// user.contact.email: bob@example.com
|
||||||
|
// user.contact.phone: N/A
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSchema_Unmarshal 演示直接使用 json.Unmarshal 创建 Schema
|
||||||
|
func ExampleSchema_Unmarshal() {
|
||||||
|
// JSON Schema 字符串
|
||||||
|
jsonStr := `{
|
||||||
|
"name": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "active"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// 直接反序列化为 Schema
|
||||||
|
var schema fieldx.Schema
|
||||||
|
json.Unmarshal([]byte(jsonStr), &schema)
|
||||||
|
|
||||||
|
// 源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"userName": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("name: %s\n", result["name"])
|
||||||
|
fmt.Printf("status: %s\n", result["status"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// name: Alice
|
||||||
|
// status: active
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSchemaFromMap 演示从 map[string]any 创建 Schema
|
||||||
|
func ExampleSchemaFromMap() {
|
||||||
|
// 先解析 JSON 到 map
|
||||||
|
jsonStr := `{
|
||||||
|
"userId": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.id"
|
||||||
|
},
|
||||||
|
"userName": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.name"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var data map[string]any
|
||||||
|
json.Unmarshal([]byte(jsonStr), &data)
|
||||||
|
|
||||||
|
// 从 map 创建 Schema
|
||||||
|
schema, _ := fieldx.SchemaFromMap(data)
|
||||||
|
|
||||||
|
// 源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 789,
|
||||||
|
"name": "Charlie",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("userId: %v\n", result["userId"])
|
||||||
|
fmt.Printf("userName: %s\n", result["userName"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// userId: 789
|
||||||
|
// userName: Charlie
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleSchema_allFieldTypes 演示所有字段类型的使用
|
||||||
|
func ExampleSchema_allFieldTypes() {
|
||||||
|
// 定义包含所有字段类型的 Schema
|
||||||
|
schema := fieldx.Schema{
|
||||||
|
// string 类型:固定字符串值
|
||||||
|
"version": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeString,
|
||||||
|
Value: "1.0.0",
|
||||||
|
},
|
||||||
|
// field 类型:从源数据获取值
|
||||||
|
"title": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "documentTitle",
|
||||||
|
},
|
||||||
|
// object 类型:嵌套对象
|
||||||
|
"metadata": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeObject,
|
||||||
|
Fields: fieldx.Schema{
|
||||||
|
"created": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "createdAt",
|
||||||
|
},
|
||||||
|
"author": fieldx.Field{
|
||||||
|
Type: fieldx.FieldTypeField,
|
||||||
|
Value: "author",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"documentTitle": "API Documentation",
|
||||||
|
"createdAt": "2024-01-01",
|
||||||
|
"author": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成对象
|
||||||
|
result, _ := schema.Generate(source)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("version: %s\n", result["version"])
|
||||||
|
fmt.Printf("title: %s\n", result["title"])
|
||||||
|
metadata := result["metadata"].(map[string]any)
|
||||||
|
fmt.Printf("metadata.created: %s\n", metadata["created"])
|
||||||
|
fmt.Printf("metadata.author: %s\n", metadata["author"])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// version: 1.0.0
|
||||||
|
// title: API Documentation
|
||||||
|
// metadata.created: 2024-01-01
|
||||||
|
// metadata.author: Alice
|
||||||
|
}
|
||||||
162
fieldx/schema.go
Normal file
162
fieldx/schema.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package fieldx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.fsdpf.net/go/reflux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldType 定义字段类型
|
||||||
|
type FieldType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FieldTypeString 固定字符串值
|
||||||
|
FieldTypeString FieldType = "string"
|
||||||
|
// FieldTypeField 从传入对象中获取字段值
|
||||||
|
FieldTypeField FieldType = "field"
|
||||||
|
// FieldTypeObject 嵌套对象类型
|
||||||
|
FieldTypeObject FieldType = "object"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Field 定义单个字段的 Schema
|
||||||
|
type Field struct {
|
||||||
|
// Type 字段类型: string, field, object
|
||||||
|
Type FieldType `json:"type"`
|
||||||
|
// Value 字段值,根据 Type 不同含义不同:
|
||||||
|
// - string: 固定字符串值
|
||||||
|
// - field: 要获取的字段路径
|
||||||
|
// - object: 忽略,使用 Fields
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
// Fields 当 Type 为 object 时,包含嵌套的字段定义
|
||||||
|
Fields Schema `json:"fields,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema 定义字段映射表
|
||||||
|
type Schema map[string]Field
|
||||||
|
|
||||||
|
// Generate 根据 Schema 生成对象
|
||||||
|
// source: 源数据对象,用于 FieldTypeField 类型获取字段值
|
||||||
|
// 返回生成的 map[string]any 对象
|
||||||
|
func (s Schema) Generate(source map[string]any) (map[string]any, error) {
|
||||||
|
// 使用 reflux 包装源数据,提供统一的访问接口
|
||||||
|
rfx := reflux.New(source)
|
||||||
|
|
||||||
|
result := make(map[string]any)
|
||||||
|
err := s.generateFields(rfx, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateFields 递归生成字段
|
||||||
|
func (s Schema) generateFields(source reflux.R, target map[string]any) error {
|
||||||
|
for fieldName, field := range s {
|
||||||
|
value, err := field.generateValue(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to generate field %s: %w", fieldName, err)
|
||||||
|
}
|
||||||
|
target[fieldName] = value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateValue 生成字段值
|
||||||
|
func (f Field) generateValue(source reflux.R) (any, error) {
|
||||||
|
switch f.Type {
|
||||||
|
case FieldTypeString:
|
||||||
|
return f.Value, nil
|
||||||
|
case FieldTypeField:
|
||||||
|
return getFieldValue(f.Value, source)
|
||||||
|
case FieldTypeObject:
|
||||||
|
// 嵌套对象
|
||||||
|
nestedResult := make(map[string]any)
|
||||||
|
err := f.Fields.generateFields(source, nestedResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nestedResult, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported field type: %s", f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFieldValue 从源对象获取字段值
|
||||||
|
// 支持点号分隔的路径,如 "user.name"
|
||||||
|
func getFieldValue(path string, source reflux.R) (any, error) {
|
||||||
|
// 分割路径
|
||||||
|
parts := strings.Split(path, ".")
|
||||||
|
|
||||||
|
// 检查字段是否存在
|
||||||
|
if !source.Exists(parts...) {
|
||||||
|
return nil, fmt.Errorf("field not found: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取值
|
||||||
|
accessor := source.Get(parts...)
|
||||||
|
return accessor.Any(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaFromJSON 从 JSON 字符串创建 Schema
|
||||||
|
// 这是最简单直接的方式
|
||||||
|
func SchemaFromJSON(jsonStr string) (Schema, error) {
|
||||||
|
var schema Schema
|
||||||
|
err := json.Unmarshal([]byte(jsonStr), &schema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||||
|
}
|
||||||
|
return schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaFromMap 从 map[string]any 创建 Schema
|
||||||
|
// 支持从 JSON 反序列化后的 map 转换
|
||||||
|
func SchemaFromMap(data map[string]any) (Schema, error) {
|
||||||
|
schema := make(Schema)
|
||||||
|
for key, value := range data {
|
||||||
|
field, err := fieldFromMap(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
schema[key] = field
|
||||||
|
}
|
||||||
|
return schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldFromMap 从 map[string]any 创建 Field
|
||||||
|
func fieldFromMap(data any) (Field, error) {
|
||||||
|
m, ok := data.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return Field{}, fmt.Errorf("invalid field data: expected map[string]any, got %T", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
field := Field{}
|
||||||
|
|
||||||
|
// 解析 type
|
||||||
|
if typeVal, ok := m["type"]; ok {
|
||||||
|
if typeStr, ok := typeVal.(string); ok {
|
||||||
|
field.Type = FieldType(typeStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 value
|
||||||
|
if value, ok := m["value"]; ok {
|
||||||
|
if valueStr, ok := value.(string); ok {
|
||||||
|
field.Value = valueStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 fields (嵌套)
|
||||||
|
if fields, ok := m["fields"]; ok {
|
||||||
|
if fieldsMap, ok := fields.(map[string]any); ok {
|
||||||
|
nestedSchema, err := SchemaFromMap(fieldsMap)
|
||||||
|
if err != nil {
|
||||||
|
return Field{}, err
|
||||||
|
}
|
||||||
|
field.Fields = nestedSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return field, nil
|
||||||
|
}
|
||||||
499
fieldx/schema_test.go
Normal file
499
fieldx/schema_test.go
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
package fieldx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_Simple 测试从 JSON 创建简单 Schema
|
||||||
|
func TestSchemaFromJSON_Simple(t *testing.T) {
|
||||||
|
jsonStr := `{
|
||||||
|
"name": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "active"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
schema, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromJSON() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["name"].Type != FieldTypeField {
|
||||||
|
t.Errorf("name.Type = %v, want %v", schema["name"].Type, FieldTypeField)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["status"].Type != FieldTypeString {
|
||||||
|
t.Errorf("status.Type = %v, want %v", schema["status"].Type, FieldTypeString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成
|
||||||
|
source := map[string]any{
|
||||||
|
"userName": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["name"] != "Alice" {
|
||||||
|
t.Errorf("result[name] = %v, want Alice", result["name"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["status"] != "active" {
|
||||||
|
t.Errorf("result[status] = %v, want active", result["status"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_Nested 测试嵌套对象
|
||||||
|
func TestSchemaFromJSON_Nested(t *testing.T) {
|
||||||
|
jsonStr := `{
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "user_profile"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"id": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userId"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
schema, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromJSON() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["user"].Type != FieldTypeObject {
|
||||||
|
t.Errorf("user.Type = %v, want %v", schema["user"].Type, FieldTypeObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成
|
||||||
|
source := map[string]any{
|
||||||
|
"userId": 123,
|
||||||
|
"userName": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["type"] != "user_profile" {
|
||||||
|
t.Errorf("type = %v, want user_profile", result["type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
user := result["user"].(map[string]any)
|
||||||
|
if user["id"] != 123 {
|
||||||
|
t.Errorf("user.id = %v, want 123", user["id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if user["name"] != "Alice" {
|
||||||
|
t.Errorf("user.name = %v, want Alice", user["name"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_NestedPath 测试嵌套路径访问
|
||||||
|
func TestSchemaFromJSON_NestedPath(t *testing.T) {
|
||||||
|
jsonStr := `{
|
||||||
|
"userId": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.id"
|
||||||
|
},
|
||||||
|
"userEmail": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.contact.email"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
schema, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromJSON() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试嵌套源数据
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 456,
|
||||||
|
"contact": map[string]any{
|
||||||
|
"email": "alice@example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["userId"] != 456 {
|
||||||
|
t.Errorf("userId = %v, want 456", result["userId"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["userEmail"] != "alice@example.com" {
|
||||||
|
t.Errorf("userEmail = %v, want alice@example.com", result["userEmail"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_ComplexNested 测试复杂嵌套结构
|
||||||
|
func TestSchemaFromJSON_ComplexNested(t *testing.T) {
|
||||||
|
jsonStr := `{
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "user_profile"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"id": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userId"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"type": "object",
|
||||||
|
"fields": {
|
||||||
|
"email": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "email"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "N/A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
schema, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromJSON() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
source := map[string]any{
|
||||||
|
"userId": 123,
|
||||||
|
"email": "test@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["type"] != "user_profile" {
|
||||||
|
t.Errorf("type = %v, want user_profile", result["type"])
|
||||||
|
}
|
||||||
|
|
||||||
|
user := result["user"].(map[string]any)
|
||||||
|
if user["id"] != 123 {
|
||||||
|
t.Errorf("user.id = %v, want 123", user["id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
contact := user["contact"].(map[string]any)
|
||||||
|
if contact["email"] != "test@example.com" {
|
||||||
|
t.Errorf("contact.email = %v, want test@example.com", contact["email"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if contact["phone"] != "N/A" {
|
||||||
|
t.Errorf("contact.phone = %v, want N/A", contact["phone"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_InvalidJSON 测试无效 JSON
|
||||||
|
func TestSchemaFromJSON_InvalidJSON(t *testing.T) {
|
||||||
|
jsonStr := `{invalid json}`
|
||||||
|
|
||||||
|
_, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("SchemaFromJSON() expected error for invalid JSON, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromJSON_EmptyJSON 测试空 JSON
|
||||||
|
func TestSchemaFromJSON_EmptyJSON(t *testing.T) {
|
||||||
|
jsonStr := `{}`
|
||||||
|
|
||||||
|
schema, err := SchemaFromJSON(jsonStr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromJSON() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(schema) != 0 {
|
||||||
|
t.Errorf("schema length = %v, want 0", len(schema))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromMap_StringType 测试从 Map 创建字符串类型
|
||||||
|
func TestSchemaFromMap_StringType(t *testing.T) {
|
||||||
|
data := map[string]any{
|
||||||
|
"status": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"value": "active",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schema, err := SchemaFromMap(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromMap() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["status"].Type != FieldTypeString {
|
||||||
|
t.Errorf("status.Type = %v, want %v", schema["status"].Type, FieldTypeString)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["status"].Value != "active" {
|
||||||
|
t.Errorf("status.Value = %v, want active", schema["status"].Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromMap_FieldType 测试从 Map 创建字段类型
|
||||||
|
func TestSchemaFromMap_FieldType(t *testing.T) {
|
||||||
|
data := map[string]any{
|
||||||
|
"name": map[string]any{
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schema, err := SchemaFromMap(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromMap() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["name"].Type != FieldTypeField {
|
||||||
|
t.Errorf("name.Type = %v, want %v", schema["name"].Type, FieldTypeField)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["name"].Value != "userName" {
|
||||||
|
t.Errorf("name.Value = %v, want userName", schema["name"].Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromMap_ObjectType 测试从 Map 创建对象类型
|
||||||
|
func TestSchemaFromMap_ObjectType(t *testing.T) {
|
||||||
|
data := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"type": "object",
|
||||||
|
"fields": map[string]any{
|
||||||
|
"id": map[string]any{
|
||||||
|
"type": "field",
|
||||||
|
"value": "userId",
|
||||||
|
},
|
||||||
|
"name": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"value": "default_name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
schema, err := SchemaFromMap(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SchemaFromMap() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["user"].Type != FieldTypeObject {
|
||||||
|
t.Errorf("user.Type = %v, want %v", schema["user"].Type, FieldTypeObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["user"].Fields["id"].Type != FieldTypeField {
|
||||||
|
t.Errorf("user.fields.id.Type = %v, want %v", schema["user"].Fields["id"].Type, FieldTypeField)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["user"].Fields["name"].Value != "default_name" {
|
||||||
|
t.Errorf("user.fields.name.Value = %v, want default_name", schema["user"].Fields["name"].Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchemaFromMap_InvalidData 测试无效数据
|
||||||
|
func TestSchemaFromMap_InvalidData(t *testing.T) {
|
||||||
|
data := map[string]any{
|
||||||
|
"field1": "invalid",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := SchemaFromMap(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("SchemaFromMap() expected error for invalid data, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchema_Unmarshal 测试直接使用 json.Unmarshal
|
||||||
|
func TestSchema_Unmarshal(t *testing.T) {
|
||||||
|
jsonStr := `{
|
||||||
|
"name": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "userName"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "active"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var schema Schema
|
||||||
|
err := json.Unmarshal([]byte(jsonStr), &schema)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema["name"].Type != FieldTypeField {
|
||||||
|
t.Errorf("name.Type = %v, want %v", schema["name"].Type, FieldTypeField)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成
|
||||||
|
source := map[string]any{
|
||||||
|
"userName": "Alice",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["name"] != "Alice" {
|
||||||
|
t.Errorf("result[name] = %v, want Alice", result["name"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchema_UnmarshalInStruct 测试在结构体中使用 Schema
|
||||||
|
func TestSchema_UnmarshalInStruct(t *testing.T) {
|
||||||
|
type Config struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Schema Schema `json:"schema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonStr := `{
|
||||||
|
"name": "test_config",
|
||||||
|
"schema": {
|
||||||
|
"userId": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.id"
|
||||||
|
},
|
||||||
|
"userName": {
|
||||||
|
"type": "field",
|
||||||
|
"value": "user.name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
err := json.Unmarshal([]byte(jsonStr), &config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Name != "test_config" {
|
||||||
|
t.Errorf("config.Name = %v, want test_config", config.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Schema["userId"].Type != FieldTypeField {
|
||||||
|
t.Errorf("userId.Type = %v, want %v", config.Schema["userId"].Type, FieldTypeField)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试生成
|
||||||
|
source := map[string]any{
|
||||||
|
"user": map[string]any{
|
||||||
|
"id": 123,
|
||||||
|
"name": "Alice",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := config.Schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["userId"] != 123 {
|
||||||
|
t.Errorf("userId = %v, want 123", result["userId"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["userName"] != "Alice" {
|
||||||
|
t.Errorf("userName = %v, want Alice", result["userName"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchema_Generate_FieldNotFound 测试字段不存在的情况
|
||||||
|
func TestSchema_Generate_FieldNotFound(t *testing.T) {
|
||||||
|
schema := Schema{
|
||||||
|
"name": Field{
|
||||||
|
Type: FieldTypeField,
|
||||||
|
Value: "nonExistentField",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
source := map[string]any{
|
||||||
|
"otherField": "value",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := schema.Generate(source)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Generate() expected error for non-existent field, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSchema_Generate_AllTypes 测试所有字段类型组合
|
||||||
|
func TestSchema_Generate_AllTypes(t *testing.T) {
|
||||||
|
schema := Schema{
|
||||||
|
"fixedValue": Field{
|
||||||
|
Type: FieldTypeString,
|
||||||
|
Value: "constant",
|
||||||
|
},
|
||||||
|
"dynamicValue": Field{
|
||||||
|
Type: FieldTypeField,
|
||||||
|
Value: "inputValue",
|
||||||
|
},
|
||||||
|
"nestedObject": Field{
|
||||||
|
Type: FieldTypeObject,
|
||||||
|
Fields: Schema{
|
||||||
|
"innerFixed": Field{
|
||||||
|
Type: FieldTypeString,
|
||||||
|
Value: "inner_constant",
|
||||||
|
},
|
||||||
|
"innerDynamic": Field{
|
||||||
|
Type: FieldTypeField,
|
||||||
|
Value: "inputValue",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
source := map[string]any{
|
||||||
|
"inputValue": "test_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := schema.Generate(source)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["fixedValue"] != "constant" {
|
||||||
|
t.Errorf("fixedValue = %v, want constant", result["fixedValue"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if result["dynamicValue"] != "test_value" {
|
||||||
|
t.Errorf("dynamicValue = %v, want test_value", result["dynamicValue"])
|
||||||
|
}
|
||||||
|
|
||||||
|
nested := result["nestedObject"].(map[string]any)
|
||||||
|
if nested["innerFixed"] != "inner_constant" {
|
||||||
|
t.Errorf("innerFixed = %v, want inner_constant", nested["innerFixed"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if nested["innerDynamic"] != "test_value" {
|
||||||
|
t.Errorf("innerDynamic = %v, want test_value", nested["innerDynamic"])
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user