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

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

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

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

Q&A

解決済

1回答

2621閲覧

Go言語でジェネリクスもしくはAny型の実装方法

tuioku

総合スコア42

Go

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

0グッド

1クリップ

投稿2021/04/15 05:22

下記の箇所でエラーになっています。型でネストもしくはネストじゃないという型を入れたいのですがエラーが出てしまいます。
プロパティにもう一つネストのオブジェクトにしたい場合って別から構造体を作らないとダメでしょうか??

説明がちょっと難しいのですが、コードを書きます。

■エラー内容
Invalid operation: hoges[name]["itemType"] == "circle" (mismatched types map[string]interface{} and string)

Go

1 2// map[string]map[string]interface{} → 「FirstName」だとエラーがでない 3var hoges map[string]map[string]map[string]interface{} 4 5// ↑ typescriptでいうジェネリクスもしくはAny 6// typescript だと map[string]map[string][any]interface{}みたいなイメージです 7 8err = json.Unmarshal([]byte(`{ 9"FirstName": { 10 "itemType": "text", 11 "x": "236", 12 "y": "145", 13 "fontSize": "20", 14 "useKerning": "false" 15 }, 16"Gender": { 17 "itemType": "circle", 18 "male": { 19 "x": "359", 20 "y": "162", 21 "width": "9", 22 "height": "9" 23 }, 24 "female": { 25 "x": "373", 26 "y": "162", 27 "width": "9", 28 "height": "9" 29 } 30 } 31 }`), &hoges) 32 33if mapArrangementCustomerData[name]["itemType"] == "circle" { 34↑ ここで波線が出る(Intellij) 35 36

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

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

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

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

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

guest

回答1

0

ベストアンサー

質問の意図がつかみかねていて、言えることは以下の3つです。

  • TypeScriptでいう「any」に相当する型はGoでは「interface{}」です
  • Goにジェネリクスはまだありません(早ければ次期リリースGo1.17に載るかもしれません)
  • JSON文字列の内容をみると受け皿であるhogesの型「map[string]map[string]map[string]interface{}」はミスマッチです

ご自身で書いている「map[string]map[string]interface{}」という型指定が正解だと思うのですが。

追記

「構造体を作らないとダメか」という問いは「どちらでも対応できます」が構造体を作るのがおすすめです。

複雑な構造をGoのJSONで扱う方法はいくつか手法があります。

  • json.RawMessageを使った多段階デコード/エンコード
  • ommitを使った未使用フィールドの省略

これらを駆使して適切な構造体を定義してやるのがGoにとっての適切なやり方になると思います。

質問の意図がつかめない原因は具体的に何をJSONで表現しようとしているのかがわからないためです。
「私だったらこうします」というアドバイスができません。

アドバイスが必要ならJSON構造の具体的な意図を解説して欲しい。

  • 各フィールドの値には具体的にどんな情報を載せたいのでしょうか?
  • itemTypeが必要な理由はなんでしょうか?

今のJSON構造は「個人の情報と解釈」すると以下の点に疑問があり意図が掴めません

  • FirstNameにValueに相当するものがないとか、
  • maleとfemaleが同時に存在するのは矛盾していて合わない気がします。

今のJSON構造を「UIのフォームレイアウト定義」すると以下の点に疑問があり意図が掴めません

  • itemType=circleに複数のmale、femaleを許容するのが不自然。

追記の追記

JSONのツリー構造を「map[string]map[string]map[string]interface{}」で受けるとき、
以下の構造を期待しますが、孫にオブジェクト以外が現れる現状のJSONデータは適合できません。
ルートがオブジェクト、子がオブジェクトという構造が守られていますが、孫はオブジェクトだったり数値だったり文字列だったりしているので、孫をオブジェクトとしてParseすることはできません。

- ルートオブジェクト - 子オブジェクト - 孫オブジェクト

孫の型がばらついているので孫は「interface{}」でで受けるべきなのです。

その上で、

go

1male := hoges["Gender"]["male"].(map[string]interface{})

としてmaleやfemaleの内容を取り出すことはできます。

追記の追記の追記

JSONがこの例示のまま可変要素が無く、構造が安定していると仮定するなら、
このJSONをJSON-to-Goに投げ込んでみましょう。

以下のような結果が得られますので、「AutoGenerated」型の変数で受けることができると思います。

go

1type AutoGenerated struct { 2 FirstName FirstName `json:"FirstName"` 3 Gender Gender `json:"Gender"` 4} 5type FirstName struct { 6 ItemType string `json:"itemType"` 7 X string `json:"x"` 8 Y string `json:"y"` 9 FontSize string `json:"fontSize"` 10 UseKerning string `json:"useKerning"` 11} 12type Male struct { 13 X string `json:"x"` 14 Y string `json:"y"` 15 Width string `json:"width"` 16 Height string `json:"height"` 17} 18type Female struct { 19 X string `json:"x"` 20 Y string `json:"y"` 21 Width string `json:"width"` 22 Height string `json:"height"` 23} 24type Gender struct { 25 ItemType string `json:"itemType"` 26 Male Male `json:"male"` 27 Female Female `json:"female"` 28}

実行例: https://play.golang.org/p/ERBnucspJha

任意のJSONを解釈したい場合

hogesの型をinterface{}にすれば任意のJSONのパースは完了します。
以下のように型スイッチを再帰的に組むことで各値にアクセスできます。

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

go

1package main 2 3import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7) 8 9const src = `{ 10 "FirstName": { 11 "itemType": "text", 12 "x": 236, 13 "y": 145, 14 "fontSize": 20, 15 "useKerning": false 16 }, 17 "Gender": { 18 "itemType": "circle", 19 "male": { 20 "x": "359", 21 "y": "162", 22 "width": "9", 23 "height": "9" 24 }, 25 "female": { 26 "x": "373", 27 "y": "162", 28 "width": "9", 29 "height": "9" 30 } 31 } 32}` 33 34func walk(s interface{}) string { 35 switch v := s.(type) { 36 case bool: 37 return fmt.Sprintf("bool(%v)", v) 38 case float64: 39 return fmt.Sprintf("float64(%f)", v) 40 case string: 41 return fmt.Sprintf("string(%q)", v) 42 case []interface{}: 43 items := []string{} 44 for _, value := range v { 45 items = append(items, walk(value)) 46 } 47 return fmt.Sprintf("[%s]", strings.Join(items, " ")) 48 case map[string]interface{}: 49 items := []string{} 50 for key, value := range v { 51 items = append(items, fmt.Sprintf("%q:%s", key, walk(value))) 52 } 53 return fmt.Sprintf("{%s}", strings.Join(items, " ")) 54 default: 55 panic("unkown type") 56 } 57} 58 59func main() { 60 var hoges interface{} 61 json.Unmarshal([]byte(src), &hoges) 62 fmt.Print(walk(hoges)) 63}

投稿2021/04/15 11:00

編集2021/04/16 11:29
nobonobo

総合スコア3367

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

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

nobonobo

2021/04/15 21:47

JSONの構造は変更は可能ですか?(例示のJSON構造はあまり良くない設計に見えます)
tuioku

2021/04/16 02:39

意図としましてはhogesという型はJsonのネスト構造がもし、ネストのネストのネストとかあっても代入できないので、それを解決したいと思っていました。 ただ、解決方法を自分で調べたところ、keyのループで値を引っこ抜いたときに、もう一度最代入することにより、エラーが回避できました。 実際に全て説明するのが難しい(コードを全て載せられないので)です。すみません。
nobonobo

2021/04/16 03:37 編集

Goによるハンドリング方法は示せますが、現状のJSONの構造設計に問題があると思います。今の構造のまま回答してもあまり実りある回答にならない気がしています。 問題はFirstNameの各フィールド値やitemTypeの値をミスマッチなmap[string]interface{}で読み取ろうとしているのがエラーになる原因です(これらはinterface{}で受け取るべき)。
tuioku

2021/04/16 03:58

構造を変えることでたしかに回避はできますが、ただ、外部のAPIとの連携とかではネストの深いが構造ががんがん来ると思います。 その場合、こちら側の都合お構いなしにJSONが返ってくると思うのですが、その場合は難しいのではないでしょうか??
tuioku

2021/04/16 03:59

Golangの弱いところは未知の構造体を扱うところかもしれませんね。
nobonobo

2021/04/16 04:38

いえ、普通はJSONの構造設計をちゃんとやればGoであっても問題になることはありませんし、その実例を示したいのですが、例示のJSON構造の意図が見えなくて・・・。 Goでなくとも動的なJSON構造をつかう場合にはセオリーがあります。 例えばitemTypeで型を示し、anyなitemValueで値を扱うという構造にするのが一般的です。
tuioku

2021/04/16 05:24

JSON構造を示せればいいのですが、分からない時があると思います。 その場合はどうすればいいでしょうか??
nobonobo

2021/04/16 07:21

全く不定のものはinterface{}で受ければ掘り下げに苦労はしますができると思いますが、 世の中のやりとりに使われているJSONはちゃんと設計されていて決まった構造というものがあるはずです。 (構造を示せないようなものをやり取りに採用する事例は実際にはないと思います) {"A":A型}, {"B":B型}のように切り替わって見えるものは実は「{"A":A型,"B":B型}」で空のものが省略されているだけという視点で見てもらえれば良いかと思います。
nobonobo

2021/04/16 07:26

で、例示のJSONでよくわからないのはitemTypeの値でその付近のフィールドセットが変化するのかどうかが知りたいです。
tuioku

2021/04/16 08:57

例えば私はjavascriptが得意ですが、jsで言えば下記みたいな感じですかね。 const something = async fetch('hogehoge'); const getResponse = JSON.parse(); Object.keys(getResponse).map((key) => { if (getResponse[key].isArray) { console.log('do something if array type'); } if (getResponse[key].isObject) { console.log('do something if object type'); } if (getResponse[key].isString) { console.log('do something if string type'); } if (getResponse[key].isNumber) { console.log('do something if string type'); } });
nobonobo

2021/04/16 11:00

「未知のJSONをパースしたい」ということであればhogesの型をinterface{}とすれば良いと思います。
tuioku

2021/04/17 02:49

なるほど。これは勉強になりますね。 Golangの場合はswitchを使うのが良さそうですね。
nobonobo

2021/04/18 00:29

「任意(未知)のJSONを解釈したい」というのが質問という解釈であってますか?
tuioku

2021/04/19 01:07

そうですね。 それで合っています。 ありがとうございます。
nobonobo

2021/04/19 01:34

それはよかったです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問