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

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

新規登録して質問してみよう
ただいま回答率
85.48%
オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

列挙型

データ型の一種で、要素・メンバなど名前のある値や、型の列挙子によって構成されます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

3回答

1779閲覧

Any型オブジェクトをDouble型に変換したい

Mama_ma_ma

総合スコア5

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

列挙型

データ型の一種で、要素・メンバなど名前のある値や、型の列挙子によって構成されます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/04/14 14:23

編集2020/04/15 01:34

前提・実現したいこと

Any型のオブジェクトをDouble型にキャストしたいのですがうまくいきません。
jsonDataはapiから受け取ったjsonが入っています。
モデルはjsonModelです。

環境
Xcode 10.3
swift 5.0

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

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

該当のソースコード

struct jsonModel: Decodable { var name: String var number: Int var value: Double var last_value: Double var gender: String var age: Int } //jsonデータの取得 public func getJson() { let url = "該当のurl" let headers: HTTPHeaders = ["Content-Type": "application/json"] Alamofire.request(url, method: .get, encoding: URLEncoding(destination: .queryString), headers: headers).responseJSON {response in guard let data = response.data else { return } do { let jsonData = try JSONDecoder().decode([jsonModel].self, from: data) } catch let error { print("Error: (error)") } } } var mergeJsonData = [String:[String:Any]]() for data in jsonData { if let _mergeJsonData = mergeJsonData[data.name] { mergeJsonData[data.name] = mergeJsonData[data.name] } else { mergeJsonData.updateValue(["value": 0, "last_value": 0, "number": 0], forKey: data.name) } if let _mergeJsonData = mergeJsonData[data.name]!["value"] { print(mergeJsonData[data.name]!["value"]!) //この時点では0が出力される let tmp = cast(value: mergeJsonData[data.name]!["value"] as! Int)! //nilエラー mergeJsonData[data.name]!["value"] = tmp + data.value } else { mergeJsonData[data.name]!.updateValue(data.value, forKey: "value") } } private func cast(value: Any) -> Double? { if let double = value as? Double { return double } return nil }

試したこと

mapを使ってキャストする方法も試してみましたがうまくいきませんでした。

swiftに触るのは初めてでここで質問するのも初めてですので、
何か足りない情報などありましたら教えてください。

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

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

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

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

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

guest

回答3

0

swift

1let tmp = cast(value: mergeJsonData[data.name]!["value"] as! Int)! //nilエラー

ここでcast()に強制アンラップ(!)をかけているのが落ちる原因です。

cast()の定義は

swift

1private func cast(value: Any) -> Double? { 2 if let double = value as? Double { 3 return double 4 } 5 return nil 6 }

となっています。

内部でDoubleにダウンキャストできない場合はnilを返すように定義しています。

一方、呼び出しでは値をIntに型変換してから呼び出していますので、cast()の戻り値はnil以外取りえません(Anyで渡しても、if let double = value as? Doubleが失敗すればnilになる)。

nilを強制アンラップすると実行時エラー(EXC_BAD_INSTRUCTION)となるので、呼び出し後に落ちているのだと思います。

この場所での強制アンラップをやめ、オプショナルバインディングするか、もしくはオプショナル型で扱いその後適切な処理をするのも一つの方法かもしれません。

しかし、問題が起きるコードを拝見すると、型変換をあちこちで行なっていて、もはや記述した人すら追えない状況になっている印象を受けます。したがって、miyabi_takatsukさんが例示されているように、JSON のデコードは JSONDecoder を使うのが余計なことを考えずに済むかと思います。

投稿2020/04/15 01:50

TsukubaDepot

総合スコア5086

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

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

Mama_ma_ma

2020/04/15 02:01

ご回答ありがとうございます。 一つ疑問なのですが、型変換しようとしているのは受け取ったJSONそのものではなく、 受け取ったJSONをループの中でAny型の辞書に格納してその後 格納したオブジェクトをキャストしています。 この場合JSONの受け取り方は関係あるのでしょうか? またJSONを受け取っているコードも追加しましたが何か問題あるように思えないのですが ご教授いただければ幸いです。
TsukubaDepot

2020/04/15 02:13

以下のコード let tmp = cast(value: mergeJsonData[data.name]!["value"] as! Int)! //nilエラー ここで、mergeJsonData[data.name]!["value"]の値をIntに強制ダウンキャストしています。 なので、cast()に値が渡される時点でInt型になってしまいます。 cast()の引数はAny型で定義してある、と思われるかもしれませんが、Int型で渡ってきた場合にはInt型として扱われます。Playgroundで実行するのが簡単でいいと思います。 たとえば、こんな感じでテストコードを書いてみると、このことがわかります。 private func cast(value: Any) -> Double? { // value の型名を表示させる print(type(of: value)) if let double = value as? Double { return double } return nil } // Any 型で定義しても、Int型の値をいれるとInt型に自動的に型変換される let a: Any = 1 let b = cast(value: a as! Int) 全てのことがこちらで実証できるわけではないので推測の域を出ませんが、Int型に強制ダウンキャストする必要がそもそもないと思います。 あと、Double から Int 型への型変換は常に失敗します。DoubleとInt型の間には直接的な継承関係がないためです。キャスト演算子(as, as! as?)で変換できるのは、直接的な継承関係があるクラスや構造体の間だけです。
Mama_ma_ma

2020/04/15 02:32

Int型への強制キャストを変更してもうまく行きませんでした。 みなさん仰るとおり受け取ったJSONデータをそのまま利用していく方法で考えたいと思います。
TsukubaDepot

2020/04/15 02:38

提示していただいたデータは、ロジック的にもても正しく動かない部分があるのがちょっと心配なのですが、それを無視したとしても jsonData[0].value といった形で直接変数にアクセスできますし、これなら何も考えなくてもDouble型としてデータがえられるので、それが一番簡単だとおもいます。
TsukubaDepot

2020/04/15 02:41

今気付いたのですが、もしかして質問者さんがやりたいことは、「配列になったJSONのデータを一つにまとめたい(valueの値を合計したい)」ということでしょうか。 それであれば、それでもっと簡単なやり方があります(辞書型のような連想配列を使わなくてもreduceでいけるはずです)。
Mama_ma_ma

2020/04/15 03:00

調べてみましたがreduceで実現できそうな気がします。 質問の仕方が悪かったです。 ありがとうございます。
TsukubaDepot

2020/04/15 03:04

いや、解決できそうであればそれでいいと思いますよ。 質問の中でやりとりをしながら、本質的な問題が見えてくることはよくあることだと思います。 reduceでうまくいかなければまた新しく質問されるといいと思います。
guest

0

ベストアンサー

プロパティ単体でキャストするのではなく、オブジェクトごとキャストする方が確実です。
また、こういった、JSONを扱う際は、そもそもJSONの形式が間違っていたりの可能性があるため、
例外処理を入れるべきです。
(do catch文を使うということ。使えば、間違っていた場合も、error文で間違いを判断しやすい)

swift

1struct jsonModel: Codable { 2 var name: String 3 var number: Int 4 // Double型のプロパティはnilを許容しておく 5 var value: Double? 6 var last_value: Double? 7 var gender: String 8 var age: Int 9} 10 11// 上記はあるクラス外、下記はあるクラス内で実行とする 12 13// jsonDataの取得時にキャストしてしまう。 14let url: URL = URL(string: "jsonを取得できるURL")! 15// 該当クラスにて、URLSessionDownloadDelegateをデリゲートする必要がある 16let task: URLSessionTask = URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in 17 18 do { 19 // jsonModel型に変換できるかをtryさせる 20 let jsonData = try JSONDecoder().decode(jsonModel.self, from: data!) 21 // クラスメソッドか何かで、jsonModel型を使った処理を行う 22 } catch error { 23 print(error) 24 } 25}) 26

投稿2020/04/15 00:52

miyabi_takatsuk

総合スコア9528

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

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

Mama_ma_ma

2020/04/15 01:40

該当のソースコードにリクエストしているコードを追記しました。 初歩的な質問で申し訳ないのですが、教えていただいたコードと私のコードではどう違いがあるのでしょうか? 現状でもjsonの取得はできているのですが、受け取ったjsonを利用して別の連想配列に 追加しています。(mergeJsonData) ループしている部分でキャストができず困っています。
miyabi_takatsuk

2020/04/15 01:53

mergeJsonDataが、[String:[String:Any]] という型(まぁ、汎用的なjson型)のままだという点です。 私の回答の意図は、mergeJsonDataをそもそも、jsonModel型にして扱うべき、ということです。 Any型って、便利なんですけど、 静的型付け言語である、Swiftでは、キャストがそもそもうまくいかなかったりと、かなり面倒なんです。 だったら、Swift側で決められた型でキャストしたときに、通るか通らないか(do ccatchさせるということ)で処理分けた方が早い、って話なのです。 もし、どうしても、値の型をAnyにしたままにしたいのであれば、 値にnilが入るパターンの処理を追加するしかないかと。 (Anyに?をつけることはできませんが)
TsukubaDepot

2020/04/15 02:02

質問者さんが作られたコードは何かを参考にされているのでしょうか。 参考にされた元記事(書籍)は重要な情報ですから、追記してもらえると助かります。 JSONdecoderで得られたデータはそのまま使えるのに、わざわざネストした辞書型にいれるという、ちょっと無駄のあるコードになっています。 もし、いくつかのサンプルを切り貼りしたとしても、その辻褄をあわせないまま貼ったのであればうまく動かないと思います。
Mama_ma_ma

2020/04/15 02:36

みなさんご回答いただきまして本当にありがとうございした。 ご指摘通りネスとしたAny型の辞書に入れるのではなく jsonModel型をそのまま利用していく方法を考えたいと思います。 ご回答が早かったmiyabi_takatsukさんをベストアンサーにさせていただきます。 ありがとうございました。
guest

0

google翻訳
Thread 1: Fatal error: オプション値のアンラップ中に予期せずnilが見つかりました

どっかの変数がnilになってるってことなんでチェックしましょう

投稿2020/04/14 14:43

y_waiwai

総合スコア87749

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

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

Mama_ma_ma

2020/04/14 14:47

AnyをDouble型に変換するところでエラーが起きてますが、printで出力すると0が入っていますので なぜnilになってしまっているのかが分からないです。
TsukubaDepot

2020/04/14 23:50

実際にnilで落ちる部分と、print()でゼロが表示される部分はどこでしょうか。
Mama_ma_ma

2020/04/15 01:11

以下を該当のソースコードに追加しました。 print(mergeJsonData[data.name]!["value"]!) //この時点では0が出力される let tmp = cast(value: mergeJsonData[data.name]!["value"] as! Int)! //nilエラー
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問