2016-12-15 16 views
0

セッションと呼ばれる独自の構造体にユーザーオブジェクト(または変数)をラップするSet関数があります。私のセッション構造体のValueフィールドに割り当てます。 Set関数はこの構造体をmarshallし、ストリングをストレージのどこかに割り当てます。ユーザー定義の構造体ポインタ{}をユーザー定義の構造体ポインタにアンマーシャリングします。

私の問題は、私は唯一の全体セッションラッパー構造体ではなく、Valueフィールドに保存されている非整列化された構造体を返すために、私のGet関数を実装するかどうかはわかりませんということです。

私は私が話して何を証明するexample非常にシンプルに作りました。

私は、ユーザーが事前に使用するために何が起こっているかの種類がわからないので、私は私のGet FUNCの代入で型アサーションを使用することはできません。

これを達成するために反射を使用する方法があると思われますか?

編集:これまでに提供された2つの回答は、私が探しているものではありません。私はどのタイプのユーザが使用されるのかわからない、それは何でもよいので、そのタイプをハードコーディングしたり、何が含まれているのかもしれないと推測しようとしたりすることはできません。

答えて

0

ユーザーは任意の値を渡すことができるかもしれませんが、コードはエラーを返すことによって無効な入力を処理できます。入力データの目的の形式がわかっている場合は、それを直接非マーシャルして、無効な入力を個別に処理できます。値は複数のタイプであるためにそれが有効なら

https://play.golang.org/p/VNCflbk3GL

package main 

import (
    "encoding/json" 
    "fmt" 
) 

type session struct { 
    Value Person 
    Flash map[string]string 
} 

type Person struct { 
    Name string 
    Age int 
} 

func Get(marshaled string) (Person, error) { 
    var sess session 
    err := json.Unmarshal([]byte(marshaled), &sess) 
    if err != nil { 
     return Person{}, err 
    } 
    fmt.Println(sess) // {{bob 3} map[]} 
    return sess.Value, nil 
} 

func main() { 
    person, err := Get(`{"Value":{"Name":"bob","Age":3},"Flash":null}`) 
    if err != nil { 
     fmt.Println("Got err:", err) 
    } 
    fmt.Printf("%#v", person) // main.Person{Name:"bob", Age:3} 
} 

は、その後、あなたがどこかのタイプのアサーションを行う必要があります。これは、対処するのは難しい中間インターフェースを{}持ってする必要がなくなります。ゴーでは、それはしかしすべてが苦痛ではありません。

https://newfivefour.com/golang-interface-type-assertions-switch.html

switch v := anything.(type) { 
case string: 
    fmt.Println(v) 
case int32, int64: 
    fmt.Println(v) 
case SomeCustomType: 
    fmt.Println(v) 
default: 
    fmt.Println("unknown") 
} 
+0

私はこの問題を持っている理由である、残念ながら着信の種類を知りません。渡される型は何でもかまいません。構造体にすることも変数にすることもできます。これはユーザーが自分で定義したものです(これはWebフレームワークライブラリのためのものです)。 – b0xxed1n

0

あなたの問題は価値のあなたの着信データ型がマップ[文字列]インターフェース{}であるということです、そしてネイティブ/直接的な方法が行きにありませんあなたのタイプにマップを変換します(間違いなくそこにコードがあります)。

OK。 Valueフィールドで受信データを完全に制御できないと仮定しても、その属性の組み合わせでデータ型を識別できますか?定義上、可能なオプションを知っておく必要があります。私たちはinterface {}の代わりに汎用のincomingオブジェクトを作成することができます。 AWSは、少なくともDynamoDBサービスのために、Go SDKで同様のアプローチを使用しています。https://github.com/aws/aws-sdk-go/blob/master/service/dynamodb/examples_test.go#L32

したがって、UnknownObj構造体にはオプションの属性が埋め込まれている可能性があります。 json.Unmarshalで。スイッチを介して配信されたフィールドが分​​かれば、送信されたデータを推測できます。ルート/値フィールドのコントロールを持っていて、すべての値の下にプッシュするのではなく、タイプごとにあなたの特定のフィールドを送信することを要求することができる場合

package main 

import (
    "encoding/json" 
    "fmt" 
) 

type session struct { 
    Value UnknownObj 
    Flash map[string]string 
} 

type UnknownObj struct { 
    Name   *string 
    Age   *float64 
    SomeOtherField *map[string]string 
} 

func Get() UnknownObj { 
    marshalledString := `{"Value":{"Name":"bob","Age":3},"Flash":null}` 

    var sess session 
    json.Unmarshal([]byte(marshalledString), &sess) 
    return sess.Value 
} 

func main() { 

    v := Get() 

    switch { 
    case v.Name != nil && v.Age != nil: 
     fmt.Println("This is a Person") 
    default: 
     fmt.Println("Unknown data type") 

    } 

} 

しかし、あなたが持っている可能性があり:

type session struct { 
    Person *Person 
    Car  *Car 
    Building *Buidling 
    Etc  *Etc 

    ... 
} 

この方法では、あなたのソリューションはさらに簡単になります。>どのプロパティがnilでないかを確認するだけです。

これが役に立ちます。

乾杯。フレームワークについてのあなたのコメントに返信する

アップデート2016年12月15日 :あなたが記述しているものは、任意のデータ型にユーザーの要求の結合のプロセスです。 OK。残念ながら、そのあまりにも多くのコードはここに投稿するが、ここでは出発点としてリンクです:これはここに結合するために使用されているパッケージとアプローチジンのフレームワークである https://github.com/go-playground/validator/blob/v8.18.1/validator.go#L498

https://github.com/gin-gonic/gin/blob/master/binding/json.go

幸運を!

+0

残念ながら私は彼らが渡すことができるものは全く考えていません。渡されるのはユーザー定義であり、文字通り何でも構いません(構造体、変数、1000階層のネスティングなど) 。これはWebフレームワークライブラリのためのものなので、ユーザーはセッションに格納する任意のタイプのオブジェクトを定義できます。 – b0xxed1n

+0

@ b0xxed1n これが完成しました。上記の私の更新をチェックしてください!おかげさまで – doharlem

1

OK、あなたがしたいことがわかっていると思います。私はこの答えがConverting map to structであることを発見し、あなたの特定のユースケースでうまくいくようにいくつか微調整を行いました。注:これは徹底的にテストされていないと少し不安定であってもよいし、ご自身の責任で使用します。

package main 

import (
    "bytes" 
    "encoding/json" 
    "errors" 
    "fmt" 
    "log" 
    "reflect" 
) 

type session struct { 
    Value interface{} 
    Flash map[string]string 
} 

type Person struct { 
    Name string 
    Age int 
} 

func Get(pointer interface{}) { 
    marshalledString := `{"Value":{"Name":"bob","Age":3},"Flash":null}` 

    var sess session 

    d := json.NewDecoder(bytes.NewBuffer([]byte(marshalledString))) 
    d.UseNumber() 
    if err := d.Decode(&sess); err != nil { 
     panic(err) 
    } 

    fmt.Printf("%#v", sess) 

    switch sess.Value.(type) { 
    case map[string]interface{}: 
     err := FillStruct(sess.Value.(map[string]interface{}), pointer) 
     if err != nil { 
      log.Fatal(err) 
     } 
    default: 
     return // You may want to return an error here... 
    } 
} 

func main() { 
    var personObj Person 

    Get(&personObj) 

    // Wanting to see personObj here have Name "bob" and Age 3 
    fmt.Printf("%#v", personObj) 
} 

func SetField(obj interface{}, name string, value interface{}) error { 
    structValue := reflect.ValueOf(obj).Elem() 
    structFieldValue := structValue.FieldByName(name) 

    if !structFieldValue.IsValid() { 
     return fmt.Errorf("No such field: %s in obj", name) 
    } 

    if !structFieldValue.CanSet() { 
     return fmt.Errorf("Cannot set %s field value", name) 
    } 

    structFieldType := structFieldValue.Type() 
    val := reflect.ValueOf(value) 

    if _, ok := value.(json.Number); ok { 
     if f, err := value.(json.Number).Int64(); err == nil { 
      structFieldValue.SetInt(f) 
      return nil 
     } 
     if f, err := value.(json.Number).Float64(); err == nil { 
      structFieldValue.SetFloat(f) 
      return nil 
     } 
    } 

    if structFieldType != val.Type() { 
     return errors.New(fmt.Sprintf("Provided value type [%s] didn't match obj field type [%s]", val.Type().String(), structFieldType.String())) 
    } 

    structFieldValue.Set(val) 
    return nil 
} 

func FillStruct(m map[string]interface{}, s interface{}) error { 
    for k, v := range m { 
     err := SetField(s, k, v) 
     if err != nil { 
      return err 
     } 
    } 
    return nil 
} 
+0

これは1つの解決策ですが、どのように頑強であるかはわかりません(特にネストされた構造体などを扱う必要がある場合)。私がしたことは、デコードを遅らせるために、ユーザーの構造体をインターフェイス{}の代わりにjson.RawMessageとして格納し、次に1回ではなく2回デコードすることです。 2番目のデコードでは、RawMessageをユーザーのポインタに直接デコードすることができます。 – b0xxed1n

関連する問題