質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

2384閲覧

Goのreflect.valueのEnum()で参照している変数のポインタ取得方法について

golang-student

総合スコア8

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2017/12/29 10:53

前提・実現したいこと

reflectの動きを理解すること。その手段として、次のようなお題を立てました。

<お題>
{"key1": []} というJSONをjson.unmarshalでinterface[]に取り込み、その結果を引数として受け取り、{"key1": [“update”]} に変更する関数「Add」を作成する。

発生している問題・エラーメッセージ

下記のコードの※1の箇所で次のエラーが発生します。

panic: reflect: reflect.Value.Set using unaddressable value

該当のソースコード

package main import ( "fmt" "encoding/json" "reflect" ) func main() { strDst := `{"key1": []}` strAdd := `"update"` var objDst interface{} var objAdd interface{} json.Unmarshal(([]byte)(strDst), &objDst) json.Unmarshal(([]byte)(strAdd), &objAdd) Add(&objDst, objAdd) } func Add(dst interface{}, add interface{}){ v := reflect.ValueOf(dst).Elem() // Merge()で宣言したobjDstのポインタ v = v.Elem().MapIndex(reflect.ValueOf("key1")) // ※2 多段構成に対応するため fmt.Println(v.Kind(), v.Elem().Kind()) // それぞれinterface, slice fmt.Println(v.Elem().Len()) // 0と表示される if v.Elem().Len() == 0 { sr := reflect.ValueOf(reflect.ValueOf(add)) v.Set(reflect.Append(v.Elem(), sr)) // ※1 } fmt.Println(v) // 以下省略 }

以下の質問に対するご回答としてmattnさんから頂いたコードをベースとしております。
https://teratail.com/questions/106644

試したこと

コードの ※2 の行をコメントアウトすれば、以下のようなシンプルな追加処理を行えることを確認しました。

成功したケースの例:
JSON [] に対する “update” の追加(追加の結果は[update])

https://blog.golang.org/laws-of-reflection にも書かれている通り、ポインタに対するreflect.valueであればSetが機能するため、期待通り動作するのだと思います(関数Addはポインタを引数で受け取っている) しかし、多段構成のJSONに対応するには ※2 のような処理でObjectから配列を取り出す必要がありますが、この処理で得られるvはポインタではないため、Setが機能しないものと理解しています。

質問

上記のようなケースにおいて、Setを実行するためにアドレス可能な値を得るにはどのようにすればよいでしょうか? あるいは配列に値を追加するSet以外の方法があれば、そちらでも構いません。お手数ですが、よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

reflectを使わずに書いたコードを意識して書くと良いでしょう。

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

go

1package main 2 3import ( 4 "fmt" 5) 6 7func main() { 8 objDst := map[string]interface{}{ 9 "key1": []interface{}{}, 10 } 11 objAdd := "update" 12 Add(objDst, objAdd) 13 fmt.Printf("%#v\n", objDst) 14} 15 16func Add(dst interface{}, value interface{}) { 17 m := dst.(map[string]interface{}) 18 s := m["key1"].([]interface{}) 19 m["key1"] = append(s, value) //#1 20}

#1でm["key1"]にアサインをやり直していることがわかると思います。
この行で「s = append(s, value)」を書いても新たなsが生まれるだけで元のdstObjに反映はされません。

なので#1に相当するreflect実装を書く必要があります。

動くように修正した例は以下です。
https://play.golang.org/p/iftGeiOMTTU

go

1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7) 8 9func main() { 10 strDst := `{"key1": []}` 11 strAdd := `"update"` 12 var objDst interface{} 13 var objAdd interface{} 14 json.Unmarshal(([]byte)(strDst), &objDst) 15 json.Unmarshal(([]byte)(strAdd), &objAdd) 16 Add(&objDst, objAdd) 17} 18 19func Add(dst interface{}, add interface{}) { 20 m := reflect.ValueOf(dst).Elem() // Merge()で宣言したobjDstのポインタ 21 v := m.Elem().MapIndex(reflect.ValueOf("key1")) // ※2 多段構成に対応するため 22 fmt.Println(v.Kind(), v.Elem().Kind()) // それぞれinterface, slice 23 fmt.Println(v.Elem().Len()) // 0と表示される 24 if v.Elem().Len() == 0 { 25 sr := reflect.ValueOf(reflect.ValueOf(add)) 26 m.Elem().SetMapIndex(reflect.ValueOf("key1"), reflect.Append(v.Elem(), sr)) // ※1 27 } 28 fmt.Println(v) 29 // 以下省略 30}

投稿2017/12/29 12:50

nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2017/12/29 12:57

ちなみにGoに慣れた人はreflectを使わず書いた事例のように書きます。reflectはトラブルシュートが難しいし極力使うのを避けるくらいの認識のほうが良いかと思います。
golang-student

2018/01/07 09:21

年末年始で返信が遅くなり恐縮です。ご丁寧なご説明ありがとうございました。頂いた2つの例についてよく理解することができました。前回の質問で分かったつもりでいましたが、いざ自分でコードを書いてみるとまた似たようなことにハマってしまいました。改めて頭を整理してみます。 また、補足して頂いた通り、たしかにreflectは保守の面で問題が多そうなので、理解できたとしても、その使い方には気をつけたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問