What I want to do is:
1. Parse model from url parameter in endpoint.(ex: media, account)
mysite.com/v1/rest/:model <- :model can be whether 'media', 'account'.
So it will look like:
mysite.com/v1/rest/media
mysite.com/v1/rest/account
2. Use 1, retrieve string and use it for getting corresponding struct.
3. Put it to the method which takes interface{}
My code looks like:
type Media struct {
Caption string
}
type Account struct {
Bio string
}
type AdminController struct {
TableName string
ID int64
}
func (c *AdminController) Get(n *core.Network) {
// I want to put struct to below GetModels method dynamically.
// Not explicitly like this.
total, data, err := c.GetModels(&Media{}, n)
// I want to do this
total, data, err := c.GetModels(caster("media"), n)
if err != nil {
n.Res.Error(err)
} else {
n.Res.Success(total, data)
}
}
Is it possible to implement method which takes string and return corresponding struct? Like this:
func (c *AdminController) caster(model string) interface{} {
if string == "media" {
return &Media{}
} else if string == "account" {
return &Account{}
}
return nil
}
If it's possible, is this a good way to deal with REST request (generic way) Or should I implement response methods one by one following table by table?
If I have to implement REST(4) * number_of_tables methods, it doesn't seem to be efficient. Any advice on this architectural problem will be appreciated.
Is there a generic helper method in Go to convert a string to the correct value based on reflect.Kind?
Or do I need to implement the switch over all kinds myself?
I have a value like "143" as a string and a reflect.Value with kind "UInt16" and like to convert that string value and set it into the UInt16 value of my struct.
My current code looks like:
func setValueFromString(v reflect.Value, strVal string) error {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(strVal, 0, 64)
if err != nil {
return err
}
if v.OverflowInt(val) {
return errors.New("Int value too big: " + strVal)
}
v.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(strVal, 0, 64)
if err != nil {
return err
}
if v.OverflowUint(val) {
return errors.New("UInt value too big: " + strVal)
}
v.SetUint(val)
case reflect.Float32:
val, err := strconv.ParseFloat(strVal, 32)
if err != nil {
return err
}
v.SetFloat(val)
case reflect.Float64:
val, err := strconv.ParseFloat(strVal, 64)
if err != nil {
return err
}
v.SetFloat(val)
case reflect.String:
v.SetString(strVal)
case reflect.Bool:
val, err := strconv.ParseBool(strVal)
if err != nil {
return err
}
v.SetBool(val)
default:
return errors.New("Unsupported kind: " + v.Kind().String())
}
return nil
}
This works already, but I wonder if this is already implemented somewhere else.
Edit: Answer to the original question ("how to obtain a reflect.Kind from its string representation") is at the end. Answer to your edited question follows:
What you're doing is the fastest and "safest". If you don't want to hassle with that big switch, you may take advantage of e.g. the json package which already contains this switch to decode values from JSON string (in encoding/json/decode.go, unexported function literalStore()).
Your decoding function could look like this:
func Set(v interface{}, s string) error {
return json.Unmarshal([]byte(s), v)
}
A simple call to json.Unmarshal(). Using / testing it:
{
var v int
err := Set(&v, "1")
fmt.Println(v, err)
}
{
var v int
err := Set(&v, "d")
fmt.Println(v, err)
}
{
var v uint32
err := Set(&v, "3")
fmt.Println(v, err)
}
{
var v bool
err := Set(&v, "true")
fmt.Println(v, err)
}
{
var v float32
err := Set(&v, `5.1`)
fmt.Println(v, err)
}
{
var v string
err := Set(&v, strconv.Quote("abc"))
fmt.Println(v, err)
}
One thing to note: when you want to pass a string, that must be quoted, e.g. with strconv.Quote(). Output (try it on the Go Playground):
1 <nil>
0 invalid character 'd' looking for beginning of value
3 <nil>
true <nil>
5.1 <nil>
abc <nil>
If you don't want to require quoted strings (which just complicates things), you may build it into the Set() function:
func Set(v interface{}, s string) error {
if t := reflect.TypeOf(v); t.Kind() == reflect.Ptr &&
t.Elem().Kind() == reflect.String {
s = strconv.Quote(s)
}
return json.Unmarshal([]byte(s), v)
}
And then you may call it with the address of a string variable and a string value unquoted:
var v string
err := Set(&v, "abc")
fmt.Println(v, err)
Try this variant on the Go Playground.
Answer to the original question: how to obtain a reflect.Kind from its string representation:
Declaration of reflect.Kind:
type Kind uint
The different values of reflect.Kinds are constants:
const (
Invalid Kind = iota
Bool
Int
Int8
// ...
Struct
UnsafePointer
)
And the reflect package provides only a single method for the reflect.Kind() type:
func (k Kind) String() string
So as it stands, you cannot obtain a reflect.Kind from its string representation (only the reverse direction is possible by using the Kind.String() method). But it's not that hard to provide this functionality.
What we'll do is we build a map from all the kinds:
var strKindMap = map[string]reflect.Kind{}
We init it like this:
func init() {
for k := reflect.Invalid; k <= reflect.UnsafePointer; k++ {
strKindMap[k.String()] = k
}
}
This is possible and correct because constants are initialized using iota which evaluates to successive untyped integer constants, and the first value is reflect.Invalid and the last is reflect.UnsafePointer.
And now you can obtain reflect.Kind from its string representation by simply indexing this map. A helper function which does that:
func strToKind(s string) reflect.Kind {
k, ok := strKindMap[s]
if !ok {
return reflect.Invalid
}
return k
}
And we're done. Testing / using it:
fmt.Printf("All: %#v\n", strKindMap)
for _, v := range []string{"Hey", "uint8", "ptr", "func", "chan", "interface"} {
fmt.Printf("String: %q, Kind: %v (%#v)\n", v, strToKind(v), strToKind(v))
}
Output (try it on the Go Playground):
All: map[string]reflect.Kind{"int64":0x6, "uint8":0x8, "uint64":0xb, "slice":0x17, "uintptr":0xc, "int8":0x3, "array":0x11, "interface":0x14, "unsafe.Pointer":0x1a, "complex64":0xf, "complex128":0x10, "int":0x2, "uint":0x7, "int16":0x4, "uint16":0x9, "map":0x15, "bool":0x1, "int32":0x5, "ptr":0x16, "string":0x18, "func":0x13, "struct":0x19, "invalid":0x0, "uint32":0xa, "float32":0xd, "float64":0xe, "chan":0x12}
String: "Hey", Kind: invalid (0x0)
String: "uint8", Kind: uint8 (0x8)
String: "ptr", Kind: ptr (0x16)
String: "func", Kind: func (0x13)
String: "chan", Kind: chan (0x12)
String: "interface", Kind: interface (0x14)
I'm new to Swift 2.0 programming (coming from the world of C#), and I'm simply trying to write a function to parse some data from a string and return the result. The function itself works well, but as part of the process, the string functions may throw and error if the input string is improperly formatted and I would like to prepare a catch-all error handler for the function to deal with this. Here's the code for the original function without error handling:
// Parses the numeric value of the BATT return string
private func parseBatteeryLevel(inputStr : String) -> Int {
if inputStr.hasPrefix("BATT") && inputStr.containsString("%") {
let start = inputStr.startIndex.advancedBy(5)
let end = inputStr.characters.indexOf("%")?.advancedBy(-1)
if end != nil {
return Int(inputStr.substringWithRange(start...end!))!
}else{
return 0
}
}else{
printStatus("Return Value Parse Error: \"\(inputStr)\"")
return 0
}
}
As you can see, I did implement a basic sanity check for the strings that could be inputted but it is by no means exaustive. My primary concern is what happens if is passes the checks, but causes the advanceBy() or any of the internal string functions to fail?
I did try to wrap the code in a do-catch block as shown below,
private func parseBatteeryLevel(inputStr : String) -> Int {
if inputStr.hasPrefix("BATT") && inputStr.containsString(" ") {
do {
let start = try inputStr.startIndex.advancedBy(5)
let end = try inputStr.characters.indexOf("%")?.advancedBy(-1)
if end != nil {
return try Int(inputStr.substringWithRange(start...end!))!
}else{
return 0
}
} catch let error as NSError {
printStatus(error.description)
}
}else{
printStatus("Return Value Parse Error: \"\(inputStr)\"")
return 0
}
}
but then I get warnings, next to all the lines inside the do block, marked with the try keyword saying,
No calls to throwing functions occur within 'try' expression
Then, there is another warning next to the catch keyword saying,
'catch' block is unreachable because no errors are thrown in 'do' block
This doesn't make sense because the function clearly crashes the program if an invalid string is entered. This would be painfully simple to deal with in C# but what is the proper way to handle this in Swift 2.0?
So the problem String startIndex and characters functions do not throw and exception that can be caught by the swift do try catch block. See The Swift Programming Language (Swift 2.1) - Error Handling documentation for more info.
So I rewrote the code to deal with parsing of the integer without crashing
func parseBatteeryLevel(inputStr : String) -> Int {
if inputStr.hasPrefix("BATT") && inputStr.containsString("%") {
let start = inputStr.startIndex.advancedBy(5)
let indexOfPercent = inputStr.characters.indexOf("%")
if (indexOfPercent != nil) {
let end = indexOfPercent!.advancedBy(-1)
let batteryLevel = inputStr.substringWithRange(start...end)
if let level = Int(batteryLevel) {
return level
}
}
return 0
} else {
print("Return Value Parse Error: \"\(inputStr)\"")
return 0
}
}
I am having difficulty understanding the relationship between interfaces and structs in go. I have declared an interface called Datatype as follows:
package main
type Datatype interface {
Unmarshal(record []string) error
String() string
}
I have also created several structs that implement this interface. Here is one simple example:
package main
import (
"encoding/csv"
"fmt"
"gopkg.in/validator.v2"
"reflect"
"strconv"
"time"
)
type User struct {
Username string `validate:"nonzero"`
UserId string `validate:"nonzero"`
GivenName string `validate:"nonzero"`
FamilyName string `validate:"nonzero"`
Email string `validate:"regexp=^[0-9a-zA-Z]+#[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)+$"`
SMS string `validate:"nonzero"`
Phone string `validate:"min=10"`
DateOfBirth time.Time
}
type Users []User
func (u *User) Unmarshal(record []string) error {
s := reflect.ValueOf(u).Elem()
if s.NumField() != len(record) {
return &FieldMismatch{s.NumField(), len(record)}
}
for i := 0; i > s.NumField(); i++ {
f := s.Field(i)
switch f.Type().String() {
case "string":
f.SetString(record[i])
case "int", "int64":
ival, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(ival)
default:
return &UnsupportedType{f.Type().String()}
}
}
return nil
}
func (u *User) String() string {
return fmt.Sprintf("%#v", u)
}
func (u *User) populateFrom(reader *csv.Reader) (Users, error) {
var users Users
for {
record, err := reader.Read()
check(err)
err = u.Unmarshal(record)
check(err)
valid := validator.Validate(u)
if valid == nil {
user := *u
users = append(users, user)
} else {
fmt.Println("Validation error?: ", valid)
}
}
return users, nil
}
Problem:
As you can see, I also have a type called Users which is just []User. When I try to return this type from a function that has a return type of []Datatype, I get the following error message:
cannot use results (type Users) as type []Datatype in return argument
I'm sure I'm missing something obvious but it seems to me that this should work.
Question:
Could someone please explain why it does not work? Is there a better (more idiomatic) way to achieve this end result?
Slices are not covariant; even though User implements Datatype, []User does not implement []Datatype (because nothing implements []Datatype: it itself is not an interface type, it's just a slice type whose element type is an interface type).
Edited to add: As Dave C points out in a comment above, a closely-related question appears in the Go FAQ. [link] The Go FAQ is licensed in a way that's compatible with Stack Exchange content, so, here's the question in its entirety:
Can I convert a []T to an []interface{}?
Not directly, because they do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
Is it possible to get the string value from a pointer to a string?
I am using the goopt package to handle flag parsing and the package returns *string only. I want to use these values to call a function in a map.
Example
var strPointer = new(string)
*strPointer = "string"
functions := map[string]func() {
"string": func(){
fmt.Println("works")
},
}
//Do something to get the string value
functions[strPointerValue]()
returns
./prog.go:17:14: cannot use strPointer (type *string)
as type string in map index
Dereference the pointer:
strPointerValue := *strPointer
A simple function that first checks if the string pointer is nil would prevent runtime errors:
func DerefString(s *string) string {
if s != nil {
return *s
}
return ""
}
Generic https://stackoverflow.com/a/62790458/1079543 :
func SafeDeref[T any](p *T) T {
if p == nil {
var v T
return v
}
return *p
}