Skip to content

Commit 188b591

Browse files
committed
Add JSON structure validation to DynamicUnmarshal
Introduced a new function, `validateJsonStructure`, to ensure the JSON data matches the schema before dynamic unmarshalling. This addition checks for the presence, type, and structure of fields, and provides detailed error messages for better debugging.
1 parent fa8661f commit 188b591

File tree

1 file changed

+76
-2
lines changed

1 file changed

+76
-2
lines changed

x/trackgate/keeper/utils.go

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,80 @@ func ContainsOperator(operators []string, operator string) bool {
4646
return false
4747
}
4848

49+
func validateJsonStructure(schemaFields map[string]interface{}, jsonData map[string]interface{}) bool {
50+
// Ensure every field in schema is present in the JSON
51+
for key, fieldType := range schemaFields {
52+
if jsonValue, exists := jsonData[key]; exists {
53+
switch ft := fieldType.(type) {
54+
case string:
55+
if !validateFieldType(ft, jsonValue) {
56+
fmt.Printf("Field '%s' has mismatched type or invalid value.\n", key)
57+
return false
58+
}
59+
case map[string]interface{}:
60+
if nestedJsonData, ok := jsonValue.(map[string]interface{}); ok {
61+
if !validateJsonStructure(ft, nestedJsonData) {
62+
fmt.Printf("Field '%s' has mismatched structure.\n", key)
63+
return false
64+
}
65+
} else {
66+
fmt.Printf("Expected nested structure for '%s', got something else.\n", key)
67+
return false
68+
}
69+
default:
70+
fmt.Printf("Unsupported type for '%s'.\n", key)
71+
return false
72+
}
73+
} else {
74+
// Error when a required field is missing from JSON data
75+
fmt.Printf("Field '%s' is missing from the JSON data.\n", key)
76+
return false
77+
}
78+
}
79+
80+
// Check for extra fields in the JSON that aren't in the schema
81+
for key := range jsonData {
82+
if _, exists := schemaFields[key]; !exists {
83+
fmt.Printf("Extra field '%s' found in the JSON data but not in the schema.\n", key)
84+
return false
85+
}
86+
}
87+
88+
return true
89+
}
90+
91+
// Validate individual field types
92+
func validateFieldType(fieldType string, value interface{}) bool {
93+
switch fieldType {
94+
case "string":
95+
_, ok := value.(string)
96+
return ok
97+
case "int":
98+
_, ok := value.(float64) // JSON unmarshalling in Go converts numbers to float64
99+
return ok
100+
case "uint":
101+
val, ok := value.(float64)
102+
return ok && val >= 0
103+
case "bytes":
104+
_, ok := value.(string) // Assuming bytes are encoded as base64 strings
105+
return ok
106+
default:
107+
return false
108+
}
109+
}
110+
49111
func DynamicUnmarshal(schemaDef types.SchemaDef, jsonData string) (string, error) {
112+
// Temporarily unmarshal JSON into a map to validate structure
113+
var tempData map[string]interface{}
114+
if err := json.Unmarshal([]byte(jsonData), &tempData); err != nil {
115+
return "error", fmt.Errorf("failed to unmarshal JSON for validation: %w", err)
116+
}
117+
118+
// Validate JSON against struct definition
119+
if !validateJsonStructure(schemaDef.Fields, tempData) {
120+
return "error", fmt.Errorf("JSON data does not match the expected schema")
121+
}
122+
50123
// Function to recursively create fields
51124
var createFields func(fields map[string]interface{}) ([]reflect.StructField, error)
52125
createFields = func(fields map[string]interface{}) ([]reflect.StructField, error) {
@@ -93,14 +166,15 @@ func DynamicUnmarshal(schemaDef types.SchemaDef, jsonData string) (string, error
93166
// Create struct type from fields
94167
fields, err := createFields(schemaDef.Fields)
95168
if err != nil {
96-
return "", err
169+
return "error", err
97170
}
171+
98172
structType := reflect.StructOf(fields)
99173
structInstance := reflect.New(structType).Elem()
100174

101175
// Unmarshal JSON into the dynamically created struct
102176
if err := json.Unmarshal([]byte(jsonData), structInstance.Addr().Interface()); err != nil {
103-
return "", fmt.Errorf("failed to unmarshal JSON: %w", err)
177+
return "error", fmt.Errorf("failed to unmarshal JSON: %w", err)
104178
}
105179

106180
// Marshal the struct back to JSON with indentation

0 commit comments

Comments
 (0)