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

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

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

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

Q&A

解決済

1回答

3354閲覧

Swift4 Codable-Decodeについて教えてください。

45tigris

総合スコア14

Swift

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

0グッド

0クリップ

投稿2018/02/27 05:26

OpenWeatherMapより以下のJSONデータを取得しました。
JSONデータ*******************************
{
"coord”:{
“lon":133.93,
"lat":34.8
},
"weather":[{"id":801,"main":"Clouds","description":"薄い雲","icon":"02d"}],
"base":"stations",
"main":{
"temp":11.6,
”pressure":1022,
"humidity":43,
"temp_min":11,
"temp_max":12
},
"visibility":10000,
"wind":{
"speed":2.6,
"deg":270
},
"clouds":{"all":20},
"dt":1519700400,
"sys":{
"type":1,
"id":7575,
"message":0.0067,
"country":"JP",
"sunrise":1519680993,
"sunset":1519721879
},
"id":1860293,
"name":"Kanagawa",
"cod":200

}


実際に欲しいデータは
・"weather"の"main"
・"main"の"temp"、"humidity"、"temp_min"、"temp_max"
・"sys"の"sunrise"、"sunset"
なのですが、"weather"の"main"は配列になっていてどのようにすれば良いのかよくわかりません。

①まず構造体を定義する辺り、特に配列の部分がどうしてよいのか分かりません。

struct Main : Coddle { let temp : Double let pressure : Int let humidity : Int let temp_min : Double let temp_max : Double } struct Sys : Codable { let sunrise : Int let sunset : Int } struct Weather : Codable { //①配列で分からないところです }

②ボタンを押したらまずは、情報をprint()しようと思っていますが、
上手くできません。

@IBAction func getInfoBT(_ sender: Any) { let listUrl = "https://api.openweathermap.org/data/2.5/weather?q=Kanagawa,jp&lang=ja&units=metric&APPID=xxxxxxxxxxxx" //listUrlの値を文字列にする? let apiURL = URL(string: listUrl)! let myData = try! Data(contentsOf: apiURL) let myWeather = try! JSONDecoder().decode(Weather.self, from: myData) let myMain = try! JSONDecoder().decode(Main.self, from: myData) let mySys = try! JSONDecoder().decode(Sys.self, from: myData) print("天気予報: (myWeather.main)") print("湿度: (myMain.humidity)") print("現在気温: (myMain.temp)") print("最低気温: (myMain.temp_min)") print("最高気温: (myMain.temp_max)") print("日の出: (mySys.sunrise)") print("日の入: (mySys.sunset)")

myWeather、myMain、mySysがnilになっています。

御教授お願い致します。

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

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

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

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

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

guest

回答1

0

ベストアンサー

こういう感じでしょうか?

Swift

1 2let json = """ 3{ 4 "coord":{ 5 "lon":133.93, 6 "lat":34.8 7 }, 8 "weather":[{ 9 "id":801, 10 "main":"Clouds", 11 "description":"薄い雲", 12 "icon":"02d" 13 }], 14 "base":"stations", 15 "main":{ 16 "temp":11.6, 17 "pressure":1022, 18 "humidity":43, 19 "temp_min":11, 20 "temp_max":12 21 }, 22 "visibility":10000, 23 "wind":{ 24 "speed":2.6, 25 "deg":270 26 }, 27 "clouds":{ 28 "all":20 29 }, 30 "dt":1519700400, 31 "sys":{ 32 "type":1, 33 "id":7575, 34 "message":0.0067, 35 "country":"JP", 36 "sunrise":1519680993, 37 "sunset":1519721879 38 }, 39 "id":1860293, 40 "name":"Kanagawa", 41 "cod":200 42} 43""" 44 45let data = json.data(using: .utf8) 46 47// ここの名前は適当です 48struct Total: Codable { 49 let coord: Coord 50 let weather: Weather 51 let base: String 52 let main: Main 53 let visibility: Int 54 let wind: Wind 55 let clouds: Clouds 56 let dt: Int 57 let sys: Sys 58 let id: Int 59 let name: String 60 let cod: Int 61} 62 63struct Coord: Codable { 64 let lon: Double 65 let lat: Double 66} 67struct Main : Codable { 68 let temp : Double 69 let pressure : Int 70 let humidity : Int 71 let temp_min : Double 72 let temp_max : Double 73} 74struct Sys : Codable { 75 let sunrise : Int 76 let sunset : Int 77} 78struct Weather : Codable { 79 let elements: [Element] 80} 81 82extension Weather { 83 init(from decoder: Decoder) throws { 84 var elements: [Element] = [] 85 var unkeyedContainer = try decoder.unkeyedContainer() 86 while !unkeyedContainer.isAtEnd { 87 let element = try unkeyedContainer.decode(Element.self) 88 elements.append(element) 89 } 90 self.init(elements: elements) 91 } 92} 93 94// ここも名前は適当です 95struct Element: Codable { 96 let id: Int 97 let main: String 98 let description: String 99 let icon: String 100} 101 102struct Wind: Codable { 103 let speed: Double 104 let deg: Int 105} 106 107struct Clouds: Codable { 108 let all: Int 109} 110 111 112do { 113 let total = try JSONDecoder().decode(Total.self, from: data!) 114 print(total.main) 115 print(total.weather) 116 print(total.sys) 117} catch let error { 118 print(error) 119}
Main(temp: 11.6, pressure: 1022, humidity: 43, temp_min: 11.0, temp_max: 12.0) Weather(elements: [sample.Element(id: 801, main: "Clouds", description: "薄い雲", icon: "02d")]) Sys(sunrise: 1519680993, sunset: 1519721879)

投稿2018/02/27 07:15

newmt

総合スコア1277

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

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

45tigris

2018/02/27 07:25

ありがとうございます。 教えていただきたいのですが、 let json = """ { "coord":{ "lon":133.93, "lat":34.8 }, "weather":[{ "id":801, "main":"Clouds", "description":"薄い雲", "icon":"02d" }], のように値を入れているのはなぜですか? 例えば、地域を変えた場合は毎回ここを書き換えないといけないのでしょうか? decoder.unkeyedContainer()を知らないのですが、これは何をしているのでしょうか? またこれは構造体を定義した下に書くものでしょうか? 大変申し訳ありませんが、教えていただけますでしょうか?
newmt

2018/02/27 08:02

>値を入れているのはなぜですか? jsonに関してはPlaygroundで確認するためにご質問にあったJSONをそのまま引用していただけで、 APIから値を取得すればその都度取得した値が設定されます。 >decoder.unkeyedContainer()を知らないのですが、これは何をしているのでしょうか? キーがないデータ(Arrayなど)をdecodeする場合に、 構造体に対して「このJSONはキーのない配列ですよ」と教えてあげているようなイメージです。 参考記事: https://qiita.com/matsuda/items/ee6d02570f716da765e9 >またこれは構造体を定義した下に書くものでしょうか? いえ、特に決まっているわけではなく、離して書いても大丈夫です。 今回は近くにあった方が繋がりがわかりやすいかと思って下に書いています。 つたない説明ですいません。
45tigris

2018/02/28 04:39

ありがとうございます。こちらこそプログラミング初心者で申し訳ありません。Playgroundでも確認できるのですね。 newmtさんのを元に以下のようにしたのですが、上手くいきません。 ************************ // ViewController.swift // CodableTest // // Created by cs on 2018/02/23. // Copyright © 2018年 cs. All rights reserved. // import UIKit import Foundation /*OpenWeatherMap 神奈川1860291/摂氏units=metric/言語lang=ja*/ struct Total : Codable { let coord : Coord let weather : [Weather] let base : String let main : Main let visibility : Int let wind : Wind let cloud : Clouds let dt : Int let sys : Sys let id : Int let name : String let cod : Int } struct Coord : Codable { let lon : Double let lat : Double } struct Main : Codable { let temp : Double let pressure : Int let humidity : Int let temp_min : Int let temp_max: Int } struct Sys : Codable { let country : String let id : Int let message : Double let sunrise : UInt64 let sunset : UInt64 let type : Int } struct Weather : Codable { let elements: [Element] } extension Weather { init(from decoder: Decoder) throws { var elements:[Element] = [] var unkeyedContainer = try decoder.unkeyedContainer() while !unkeyedContainer.isAtEnd { let element = try unkeyedContainer.decode(Element.self) elements.append(element) } self.init(elements: elements) } } struct Element: Codable { let id:Int let main:String let description:String let icon:String } struct Wind : Codable { let deg : Int let speed : Double } struct Clouds : Codable { let all : Int } class ViewController: UIViewController { //UIボタン これを押すと天気の結果がprintされる @IBAction func getInfo(_ sender: Any) { let listUrl = "https://api.openweathermap.org/data/2.5/weather?q=Kanagawa,jp&lang=ja&units=metric&APPID=xxxxxxxxxxxxxxxxxxx" //URLを文字列に? let apiURL = URL(string: listUrl)! //文字列データの内容をmyDataに let mydata = try! Data(contentsOf: apiURL) do { let total = try JSONDecoder().decode(Total.self, from: mydata) print("データ: (total)") print("天気予報: (total.weather.description)") print("湿度: (total.main.humidity)") print("現在気温: (total.main.temp)") print("最低気温: (total.main.temp_min)") print("最高気温: (total.main.temp_max)") print("日の出: (total.sys.sunrise)") print("日の入: (total.sys.sunset)") } catch { print(error) } } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } ******************************* としたのですが、 typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [CodableTest.Total.(CodingKeys in _852CE05B3FCCA01D829FC5F6F0AE50C2).weather, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0))], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil)) と表示されてしまいます。 申し訳ありません。どうしたら良いか教えて頂けますか?
newmt

2018/02/28 07:18

ご連絡ありがとうございます。 実際にopenweatherのAPIを投げてみましたところ、 少しJSONの形が異なっていたようです。 下記のようにすると私の方では取得できましたがいかがでしょうか? struct Total : Codable { let coord : Coord let weather : [Weather] let base : String let main : Main let visibility : Int let wind : Wind let clouds : Clouds let dt : Int let sys : Sys let id : Int let name : String let cod : Int } struct Coord : Codable { let lon : Double let lat : Double } struct Main : Codable { let temp : Double let pressure : Int let humidity : Int let temp_min : Int let temp_max: Int } struct Sys : Codable { let country : String let id : Int let message : Double let sunrise : UInt64 let sunset : UInt64 let type : Int } struct Weather : Codable { let id:Int let main:String let description:String let icon:String // let elements: [Element] //let element: Element } //extension Weather { // init(from decoder: Decoder) throws { // var elements:[Element] = [] // var unkeyedContainer = try decoder.unkeyedContainer() // while !unkeyedContainer.isAtEnd { // let element = try unkeyedContainer.decode(Element.self) // elements.append(element) // } // self.init(elements: elements) // } //} //struct Element: Codable { // let id:Int // let main:String // let description:String // let icon:String //} struct Wind : Codable { let deg : Int let speed : Double } struct Clouds : Codable { let all : Int }
45tigris

2018/02/28 08:00

ありがとうございます。構造体Weather辺りが変わったのですね。早速試してみました。 keyNotFound(CodableTest.Total.(CodingKeys in _852CE05B3FCCA01D829FC5F6F0AE50C2).cloud, Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key cloud (\"cloud\").", underlyingError: nil)) と表示されてしまいます。 ちなみに let jsonData = try! JSONSerialization.jsonObject(with: mydata) print("jsonDataは (jsonData)") とすると 値が見れます。JSONDecodeを使う際はJSONSerializationは必要ないのでしょうか?
newmt

2018/02/28 08:18

すいません。一箇所細かい修正をしていたのですが、 struct Total : Codable { ... let cloud : Clouds -> let clouds : Clouds } は変更されていますでしょうか?(違っていたらごめんなさい) >JSONDecodeを使う際はJSONSerializationは必要ないのでしょうか? はい、必要ではありません。 JSONSerializationはJSONをAny型にしか変換してくれず、 それ以降の処理では、 json["キー名"] as? XXX といったようにしてJSONの構造を意識しなればいけない、 かついちいちキャストをしながら値を取得しなければなりません。 一方で、JSONDecodeではあらかじめ型を設定していますので、 Frameworkの方で自動で変換してくれます。 なので、total.weather.descriptionといったような形で 型情報を持った値の取得をすることができるようになります。
45tigris

2018/03/01 01:11

すみません!! struct Total : Codable { ... let cloud : Clouds -> let clouds : Clouds } を修正したら、できました(>v<) !! 色々教えていただき、本当にありがとうございました!! 他のAPIも取得できるように、これをベースに勉強して見ます (^v^)
45tigris

2018/03/06 07:55

先日はありがとうございました。あれからUIImageにweather[0].iconのアイコンを表示したいと試行錯誤しているのですが分かりません。 let weatherIcon = URL(string: "https://openweathermap.org/w/(image).png") としたのですが上手くいきません。 よろしければ、ご教授ください。
newmt

2018/03/06 09:13 編集

何通りか書き方があるかと思いますが、下記ではいかがでしょうか? // imageViewという変数名でUIImageViewをstoryboadから接続しているとします @IBOutlet weak var imageView: UIImageView! if let weatherIcon = URL(string: "https://openweathermap.org/w/(image).png"), let data = try? Data(contentsOf: weatherIcon), let image = UIImage(data: data) { imageView.image = image }
45tigris

2018/03/07 00:59

ありがとうございます!! UIImageViewをstoryboadから接続しています。 このif文の書き方は初めて見るので教えて頂きたいのですが、 "https://openweathermap.org/w/(image).png"という文字列をURLにしてweatherIconという定数に入れ、 wheatherIconのURLの内容をdataという定数に入れ、 dataをUIImageに変換してimageという定数に入れたら、 imageViewに表示するという解釈で合っているでしょうか? 前回のnewmtさんのコードを元に do {  let total = try JSONDecoder().decode(Total.self, from: mydata)  print("データ: (total)")  let image = "(total.weather[0].icon)" if let weatherIcon = URL(string: "https://openweathermap.org/w/(image).png"), let data = try? Data(contentsOf: weatherIcon), let imageIcon = UIImage(data: data) { weatherImageView.image = imageIcon }  print("天気予報: (total.weather.description)")  print("湿度: (total.main.humidity)")  print("現在気温: (total.main.temp)")  print("最低気温: (total.main.temp_min)")  print("最高気温: (total.main.temp_max)")  print("日の出: (total.sys.sunrise)")  print("日の入: (total.sys.sunset)")  } catch {   print(error)  } } としたのですがUIImageViewに表示することができませんでした。
45tigris

2018/03/07 04:22

とても早い回答をありがとうございます!! 表示することができました!!
newmt

2018/03/07 04:34

表示できてよかったです。 別で同じ質問をされているようですが、そちらにも回答を追加した方がよろしいでしょうか?
45tigris

2018/03/07 06:43

お手数をお掛けすると思い、「教えて頂いて解けました」と書かせていただきましたので大丈夫だと思います(^v^) お気遣いありがとうございます。 また、行き詰まりましたら質問させてください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問