Home  >  Article  >  Backend Development  >  How to extract type parameters using reflection

How to extract type parameters using reflection

王林
王林forward
2024-02-12 17:24:05575browse

How to extract type parameters using reflection

Question content

Context: I am writing a generic automapper that takes two types of structures and checks if each field of said structure has a given specified tags, then copies the values ​​from the source structure to the target structure, assuming they have matching tags and types. Whenever a struct field is another (nested) struct, I want the automapper function to perform a recursive call that automatically maps down the rabbit hole.

Problem: I can only pass the concrete type of the root structure. Once I get into the generic function using reflection, trying to extract the nested struct type seems impossible. Although I can pass value.interface() as a parameter, I still need to pass the type parameter.

Here is some simplified code to show the problem.

type Alpha struct {
    Nested Beta `automap:"nested"`
}

type Beta struct {
    Info string `automap:"info"`
}

type Foo struct {
    Nested Bar `automap:"nested"`
}

type Bar struct {
    Info string `automap:"info"`
}

func TestAutoMap(t *testing.T) {

    b := Beta{Info: "Hello from Beta!"}
    a := Alpha{Nested: b}

    f, err := AutoMap[Alpha, Foo](a)
    if err != nil {
        fmt.Println(err)
        t.Fail()
    }
    fmt.Println("f.nested.info:", f.Nested.Info)
}

func AutoMap[S, T any](source S) (target T, err error) {

    targetStruct := reflect.ValueOf(&target).Elem()
    sourceStruct := reflect.ValueOf(&source).Elem()

    // .Type and .Kind directly did not work.
    nestedSourceType := ??? // I want this to be type Beta.
    nestedTargetType := ??? // I want this to be type Bar.

    sourceInterface := sourceStruct.Interface()

    t, err := AutoMap[nestedSourceType, nestedTargetType](sourceInterface)
    if err != nil {
        return target, err
    }
    target = t

    return target, nil
}

Solution

Following the advice of @mkopriva, I would like to share a simple solution to the problem I encountered. p>

Feel free to correct or improve it, but keep in mind that I intentionally did not include the various checks and assertions below.

(go playground example)

type Alpha struct {
    NestedOnce Beta
}

type Beta struct {
    NestedTwice Gamma
}

type Gamma struct {
    Info string
}

type Foo struct {
    NestedOnce Bar
}

type Bar struct {
    NestedTwice Baz
}

type Baz struct {
    Info string
}

func TestAutoMap(t *testing.T) {

    g := Gamma{"Hello from Gamma!"}
    b := Beta{g}
    a := Alpha{b}

    f, err := AutoMap[Foo](a)
    if err != nil {
        fmt.Println(err)
        t.Fail()
    } else {
        fmt.Println("Foo.NestedOnce.NestedTwice.Info:", f.NestedOnce.NestedTwice.Info)
    }
}

func AutoMap[T any](source any) (target T, err error) {

    // Peel off 'any' from the function parameter type.
    sourceStruct := reflect.ValueOf(&source).Elem().Elem()

    targetStruct := reflect.ValueOf(&target).Elem()

    err = autoMap(sourceStruct, targetStruct)

    return target, err
}

func autoMap(s, t reflect.Value) error {

    sourceField := s.Field(0)
    targetField := t.Field(0)

    if sourceField.Kind() == reflect.Struct {
        err := autoMap(sourceField, targetField)
        if err != nil {
            return err
        }
        return nil
    }

    targetField.Set(sourceField)

    return nil
}

The above is the detailed content of How to extract type parameters using reflection. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete