简体   繁体   中英

Cast an interface using a pointer of a Slice to iterate its values

Context:

Using a custom Binder and Validator in Echo Framework. The binder use a signature of (interface{}, echo.Context) , but a pointer is always passed and checked by echo.DefaultBinder .

I'm having trouble validating an array of struct for some reason, when an array is passed for some unknown reason. Therefore, I'm trying to validate each elements in the interface, if this interface is an Array or a Slice.

Problem:

I cannot find a way to both cast the interface to a value instead of a pointer and iterate trough the values of this array to validate each of them.

My Code so far:

func (cb *CustomBinder) Bind(i interface{}, c echo.Context) error {
    db := new(echo.DefaultBinder)
    validate := validator.New()
    if err := db.Bind(i, c); err != nil {
        return err
    }

    kind := reflect.ValueOf(i).Elem().Kind()
    if kind == reflect.Array || kind == reflect.Slice {
        
        // ... Iteration and Validation

    } else {
        if err := validate.Struct(i); err != nil {
            return err
        }
    }

    return nil
}

I would rather use type assertions than reflection because reflection is slow in terms of performance and not friendly to use.
To illustrate what I mean, check this example code where I have a function that accepts an argument of type interface{} and prints the values according to the data type,

package main

import (
    "fmt"
)

func main() {
    var dynamicValue interface{}

    dynamicValue = []interface{}{"value1", "value2"}
    printValue(dynamicValue)

    dynamicValue = map[string]interface{}{"key1": "value1"}
    printValue(dynamicValue)

    dynamicValue = "value1"
    printValue(dynamicValue)

}

func printValue(i interface{}) {
    if arrayValue, isArray := i.([]interface{}); isArray {
        for index, value := range arrayValue {
            fmt.Printf("Index: %d Value: %v \n", index, value)
        }
    } else if mapValue, isMap := i.(map[string]interface{}); isMap {
        for key, value := range mapValue {
            fmt.Printf("Key: %s Value: %v \n", key, value)
        }

    } else if stringValue, isString := i.(string); isString {
        fmt.Println(stringValue)
    } else {
        fmt.Println("Invalid data type! Only supports string, arrays and maps ")
    }

}

Output:
Index: 0 Value: value1
Index: 1 Value: value2
Key: key1 Value: value1
value1

Playground: https://play.golang.org/p/TMfojVdoi5b

You can use this logic of type assertion in your code to check if the interface is a slice and iterate over it for validation.
Something like this,

func (cb *CustomBinder) Bind(i interface{}, c echo.Context) error {
    db := new(echo.DefaultBinder)
    validate := validator.New()
    if err := db.Bind(i, c); err != nil {
        return err
    }

   
   if arrayValue, isArray := i.([]interface{}); isArray {
    // Iteration
    for index, value := range arrayValue {
        // Validation
    }
    } else {
        if err := validate.Struct(i); err != nil {
            return err
        }
    }

    return nil
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM