Skip to content

Commit 9e06b7c

Browse files
authored
feat(data): enhance unmarshaler with JSON string to struct conversion (#1116)
Because - YAML multiline strings containing JSON schemas (like `response-schema: |`) could not be automatically converted to Go struct types like `genai.Schema` This commit - Adds automatic JSON string → struct conversion capability to the core unmarshaling framework - Implements fast pre-check (`stringValue[0] == '{'` or `'['`) to avoid unnecessary JSON parsing overhead on regular strings - Adds `tryUnmarshalJSONString()` helper function that uses direct `json.Unmarshal` to preserve complex nested structures
1 parent 79630c0 commit 9e06b7c

File tree

2 files changed

+410
-0
lines changed

2 files changed

+410
-0
lines changed

pkg/data/struct.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package data
33
import (
44
"container/list"
55
"context"
6+
"encoding/json"
67
"errors"
78
"fmt"
89
"reflect"
@@ -514,6 +515,18 @@ func (u *Unmarshaler) unmarshalString(ctx context.Context, v format.String, fiel
514515
}
515516
return u.unmarshalString(ctx, v, field.Elem(), structField)
516517
default:
518+
// Check if we can unmarshal JSON string into struct
519+
if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct) {
520+
// Fast pre-check: only attempt JSON parsing if string looks like JSON
521+
if len(stringValue) > 1 && (stringValue[0] == '{' || stringValue[0] == '[') {
522+
// Try to parse the string as JSON and unmarshal into the struct
523+
if u.tryUnmarshalJSONString(stringValue, field) == nil {
524+
return nil
525+
}
526+
}
527+
// If not JSON-like or parsing fails, continue with other type handling
528+
}
529+
517530
switch field.Type() {
518531
// Handle time.Duration
519532
case reflect.TypeOf(time.Duration(0)):
@@ -1192,3 +1205,24 @@ func (m *Marshaler) marshalSlice(v reflect.Value) (Array, error) {
11921205
}
11931206
return arr, nil
11941207
}
1208+
1209+
// tryUnmarshalJSONString attempts to unmarshal a JSON string directly into a struct field
1210+
func (u *Unmarshaler) tryUnmarshalJSONString(jsonStr string, field reflect.Value) error {
1211+
// Create a new instance if the field is a nil pointer
1212+
if field.Kind() == reflect.Ptr {
1213+
if field.IsNil() {
1214+
field.Set(reflect.New(field.Type().Elem()))
1215+
}
1216+
// For pointer types, unmarshal directly into the pointed-to value
1217+
return json.Unmarshal([]byte(jsonStr), field.Interface())
1218+
}
1219+
1220+
// For non-pointer struct types, we need to unmarshal into a temporary value
1221+
// then set it, because we can't get the address of field directly
1222+
tempValue := reflect.New(field.Type())
1223+
if err := json.Unmarshal([]byte(jsonStr), tempValue.Interface()); err != nil {
1224+
return err // Not valid JSON or incompatible struct
1225+
}
1226+
field.Set(tempValue.Elem())
1227+
return nil
1228+
}

0 commit comments

Comments
 (0)