Skip to content

Commit

Permalink
Merge pull request #17 from pasqal-io/yoric/bug_kv_deserializer_from_…
Browse files Browse the repository at this point in the history
…reflect

[FIX] Additional tests/fixes for KVListDeserializerFromReflect
  • Loading branch information
David Teller authored Dec 4, 2024
2 parents ce6906c + c755858 commit ea81c70
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 8 deletions.
4 changes: 2 additions & 2 deletions deserialize/deserialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,13 @@ type kvReflectDeserializer struct {
}

func (kvrd kvReflectDeserializer) DeserializeKVListTo(value kvlist.KVList, reflectOut *reflect.Value) error {
normalized := make(jsonPkg.JSON)
normalized := make(map[string]any)
err := deListMapReflect(kvrd.typ, normalized, value, kvrd.options)
if err != nil {
return err
}

err = kvrd.reflectDeserializer(reflectOut, normalized.AsValue())
err = kvrd.reflectDeserializer(reflectOut, kvlist.MakeRootDict(normalized).AsValue())
if err != nil {
return err
}
Expand Down
33 changes: 32 additions & 1 deletion deserialize/deserialize_reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ func TestNestedStructReflectKVDeserializer(t *testing.T) {
assert.Equal(t, *deserialized, sample)
}

// Not mandatory, but could be nice to have.
func TestAnonymStructReflectKVDeserializer(t *testing.T) {
type EmbeddedStruct struct {
BBB string
Expand Down Expand Up @@ -175,3 +174,35 @@ func TestAnonymStructReflectKVDeserializer(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, *deserialized, sample)
}

// A bug we encountered in previous versions: KVDeserializerFromReflect
// did not manage to see through an alias `[]TestType` = `[]string`.
func TestReflectKVDeserializerUnderlyingPrimitiveSlices(t *testing.T) {
type StringAlias string
type Int8Alias uint8
type BoolAlias bool
type TestStruct struct {
Strings []StringAlias
Int8s []Int8Alias
Bools []BoolAlias
}
sample := TestStruct{
Strings: []StringAlias{"abc"},
Int8s: []Int8Alias{1, 2, 3},
Bools: []BoolAlias{true, true, false},
}

deserializer, err := deserialize.MakeKVDeserializerFromReflect(deserialize.QueryOptions(""), reflect.TypeOf(sample))
assert.NilError(t, err)

kvList := map[string][]string{}
kvList["Strings"] = []string{"abc"}
kvList["Int8s"] = []string{"1", "2", "3"}
kvList["Bools"] = []string{"true", "true", "false"}

deserialized := new(TestStruct)
reflectDeserialized := reflect.ValueOf(deserialized).Elem()
err = deserializer.DeserializeKVListTo(kvList, &reflectDeserialized)
assert.NilError(t, err)
assert.DeepEqual(t, *deserialized, sample)
}
22 changes: 22 additions & 0 deletions deserialize/deserialize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1558,3 +1558,25 @@ func TestKVDeserializerFlattened(t *testing.T) {

assert.DeepEqual(t, *found, expected)
}

// KVListDeserializer works with arrays with non-primitive types with a primitive Kind.
func TestKVDeserializeUnderlyingPrimitiveSlices(t *testing.T) {
type TestType string
type TestStruct struct {
TestField []TestType
}

deserializer, err := deserialize.MakeKVListDeserializer[TestStruct](deserialize.QueryOptions(""))
assert.NilError(t, err)

sample := TestStruct{
TestField: []TestType{"abc"},
}

kvlist := make(map[string][]string)
kvlist["TestField"] = []string{"abc"}

deserialized, err := deserializer.DeserializeKVList(kvlist)
assert.NilError(t, err)
assert.DeepEqual(t, *deserialized, sample)
}
18 changes: 13 additions & 5 deletions deserialize/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,22 @@ func (v Value) AsDict() (shared.Dict, bool) {
}
}
func (v Value) AsSlice() ([]shared.Value, bool) {
if wrapped, ok := v.wrapped.([]any); ok {
result := make([]shared.Value, len(wrapped))
for i, value := range wrapped {
result[i] = Value{wrapped: value}
// We can't simply cast to `[]any`, as this doesn't work for e.g. `[]string`.
reflected := reflect.ValueOf(v.wrapped)
switch reflected.Type().Kind() {
case reflect.Array:
fallthrough
case reflect.Slice:
length := reflected.Len()
result := make([]shared.Value, length)
for i := 0; i < length; i++ {
value := reflected.Index(i)
result[i] = Value{wrapped: value.Interface()}
}
return result, true
default:
return nil, false
}
return nil, false
}
func (v Value) Interface() any {
return v.wrapped
Expand Down

0 comments on commit ea81c70

Please sign in to comment.