2016-10-06 4 views
3

GOにstringreflect.Kindに基づいて正しい値に変換する汎用ヘルパーメソッドがありますか?文字列の値を正しいreflect.Kindに変換する方法?

または、すべての種類のスイッチを自分で実装する必要はありますか?

私は文字列として "143"のような値を持ち、種類は "UInt16"のreflect.Valueを持ち、その文字列値を変換してそれを私の構造体のUInt16値に設定するのが好きです。

私の現在のコードは次のようになります。

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 
} 

これはすでに動作しますが、これはすでにどこか別の場所に実装されているのだろうか。

答えて

3

編集:元の質問(「そのstring表現からreflect.Kindを取得する方法」)に回答は終わりです。編集した質問への回答は次のとおりです。


何をやっていることは最速と「最も安全」です。その大きいswitchを扱いたくない場合は、たとえばjsonパッケージには、このswitchが含まれており、JSON文字列(encoding/json/decode.go、非公開関数literalStore())の値をデコードします。

func Set(v interface{}, s string) error { 
    return json.Unmarshal([]byte(s), v) 
} 

json.Unmarshal()への単純な呼び出し:

あなたのデコード機能は、次のようになります。それをテスト/使用:注意すべき

{ 
    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) 
} 

一つのこと:あなたはstringを渡したい、例えば、引用符で囲む必要がありますstrconv.Quote()となります。出力(Go Playground上でそれを試してみてください):

1 <nil> 
0 invalid character 'd' looking for beginning of value 
3 <nil> 
true <nil> 
5.1 <nil> 
abc <nil> 

あなたは(物事を複雑に)引用文字列を必要としない場合、あなたはSet()機能にそれを構築することがあります。

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) 
} 

そして、でこのバリアントを試してみてください

var v string 
err := Set(&v, "abc") 
fmt.Println(v, err) 

:あなたはstring変数のアドレスと引用符で囲まれていないstring値でそれを呼び出すことができます。そのstring表現からreflect.Kindを取得する方法:元の質問に


回答reflect.Kind

宣言:

type Kind uint 

reflect.Kind Sの異なる値は定数である。

const (
    Invalid Kind = iota 
    Bool 
    Int 
    Int8 
    // ... 
    Struct 
    UnsafePointer 
) 

そしてreflectパッケージはreflect.Kind()タイプにのみ単一のメソッドを提供しますので、そのまま

func (k Kind) String() string 

、あなただけの逆方向はKind.String()メソッドを使用することにより可能である(そのstring表現からreflect.Kindを得ることができません)。しかし、この機能を提供することは難しいことではありません。私たちがやる何

は、我々はすべての種類の中からmapを構築です:

var strKindMap = map[string]reflect.Kind{} 

我々はこのようにそれをINIT:

func init() { 
    for k := reflect.Invalid; k <= reflect.UnsafePointer; k++ { 
     strKindMap[k.String()] = k 
    } 
} 

定数を使用して初期化されているので、これは可能で正しいですiotaは、連続した型なし整数定数として評価され、最初の値はreflect.Invalidであり、最後はreflect.UnsafePointerです。

そして今度はstringの表現からreflect.Kindを得ることができます。それを行うヘルパー関数:

func strToKind(s string) reflect.Kind { 
    k, ok := strKindMap[s] 
    if !ok { 
     return reflect.Invalid 
    } 
    return k 
} 

これで完了です。テスト/それを使用して:

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)) 
} 

は出力(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) 
+0

申し訳ありませんが、私は明確ではなかった場合。私も同様に質問を編集します。私は "143"のような文字列とreflect.Valueのような種類の "UInt16"とその文字列の値を変換し、私の構造体のUInt16値に設定するように – Tarion

+0

@タリオン編集された答えを参照してください。 – icza

関連する問題