2017-10-24 18 views
1

次の要点を考慮してください:https://gist.github.com/anonymous/0703a591f97fa9f6ad35b29234805fbd - ファイルの長さが長いため、すべての関連情報を効率的に表示する方法はありません。謝罪JSONオブジェクトのデコード(パラメータ付きGETリクエストの送信) - スウィフト4デコード可能

デコードしようとしているネストされたJSONオブジェクトがいくつかあります。

{ 
    "establishments":[ 
     { ... } 
    ], 
    "meta":{ ... }, 
    "links":[ 

    ] 
} 

^establishments配列は、私のコードは、私が実際にスウィフト4のJSONDecode()機能を利用していますViewController.swiftの7行目まで正常に実行されEstablishmentオブジェクト

が含まれています。

ViewController.swift

let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))" 

guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return } 

URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in 
    guard let data = data, error == nil, response != nil else { 
     print("Something went wrong") 
     return 
    } 
    //print("test") 
    do { 
     let establishments = try JSONDecoder().decode(Establishments.self, from: data) 
     print(establishments) 
    } catch { 
     print("summots wrong") 
    } 

}).resume() 

IはEstablishment.swiftで見マッピングさJSONオブジェクトの取得が出力される:summots wrong - ViewController.swiftのライン11上に。

{ 
    "establishments":[ ... ], 
    "meta":{ ... }, 
    "links":[ ] 
} 

が、私のクラス構造は、/私は私のビューコントローラで何をやっている場合、私は知らない。

私はJSON形式はとても似ているので、期待どおりに動作しない理由があると信じてこのレイアウト基準を満たす。

どこに間違っているのでしょうか?私はそれが非常に基本的なものだと思うが、私はそれのまわりで私の頭を取得するように見えることはできません

私も、私は名前の大文字とクラスの属性になっていないよということを理解 - 任意の規則

を破壊するための謝罪編集:私の代わりに自分の文のエラーを印刷し、それは次のとおりです。

の代わりに:print("summots wrong")

私が書いた:print(error)

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}))) 

Hamishが正しく指摘したように、私はGETリクエストから正しいJSONデータを受け取っていません。実際、私はJSONを全く受け取っていません。 x-api-version2に設定されていない場合、私が使用しているAPIはXML形式にデフォルト設定されています。私のJSON出力を取り出すためにx-api-version: 2accept: application/jsoncontent-type: application/json:私は次のヘッダーとポストマンを使用print(String(data: data, encoding: .utf8))ライン

:私のViewController.swiftファイルで

代わりの//print("test")

は、私がお勧めを使用しました。ブラウザウィンドウで同じGET文字列(すべてのパラメータを正しく入力したもの)を使用すると、XML形式の The API 'Establishments' doesn't existというエラーが表示されます。

私はこのURLでヘッダーを送信する方法はありますか?

編集2:URLではなく、URLRequestの使用を変更しました。ここで

私の更新ViewController.swiftコードです:私はエラーを取得しています

let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))" 

guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return } 
var request = URLRequest(url: url) 
request.httpMethod = "GET" 
request.addValue("x-api-version", forHTTPHeaderField: "2") 
request.addValue("accept", forHTTPHeaderField: "application/json") 
request.addValue("content-type", forHTTPHeaderField: "application/json") 

URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in 
    guard let data = data, error == nil, response != nil else { 
     print("Something went wrong") 
     return 
    } 
    print(String(data: data, encoding: .utf8)) 
// do { 
//  let establishments = try JSONDecoder().decode(Establishments.self, from: data) 
//  print(establishments) 
// } catch { 
//  print(error) 
// } 

}).resume() 

HTTP Error 400. The request has an invalid header name

私はいくつかのチェックをした、それは要求がちょうど2

x-api-versionヘッダー値を無視します

アイデア

EDIT 3:addValueの正しい形式がされている必要があります : request.addValue("2", forHTTPHeaderField: 'x-api-versionのように。

私は今、私のクラス宣言、特に私の 'メタ'クラスの問題に直面しています。

Meta.(CodingKeys in _DF4B170746CD5543281B14E0B0E7F6FB).dataSource], debugDescription: "Expected String value but found null instead.", underlyingError: nil

私のクラスの宣言は、それがJSONオブジェクトから期待のデータをマッチングされていないので、それは文句を言っています。

私のJSON応答(要旨を参照)において、2つのmetaオブジェクトがあり、一つはdataSourceためnullの値を含み、他方はLuceneあります。

デコード可能なプロトコルに準拠して、私のクラス初期化子の文字列と同様に 'null'値を受け入れる方法はありますか?

JSONレスポンスのその部分は私には死んでいますが、それでもオブジェクトを作成する必要はありますか、それとも無視するだけでいいですか? - 何も宣言することなく。または、データを使用できるようにするためにJSONレスポンスのすべてをメモする必要がありますか?

+0

また、要点にリンクするのではなく、コードの関連部分(好ましくは[mcve])を質問本体にコピーして貼り付けてください。私たちはあなたの質問を理解するためにこのページを離れることを期待されるべきではありません。 – Hamish

+0

建設的な批評家のおかげで、私は質問にいくつかの編集を加えました –

+0

私はJSONバリデーターを通してJSON出力を実行しましたが、すべてが有効であるようです。何か案は? :\ –

答えて

1

自動的にデコードするには、モデルクラスプロパティを応答値タイプと一致させる必要があります。 JSONレスポンスから、JSON値タイプと一致しないデータ・モデル・プロパティーはほとんど見られません。例:

//inside response 
"Hygiene": 10, // which is integer type 

// properties in Scores 
let Hygiene: String 

モデルプロパティのプロパティをクラスプロパティとして応答またはJSONレスポンスとして変更する必要があります。実証するための

、私は次のように変更して、あなたのモデルクラスを変更した:

 // changed from String 
     let Hygiene: Int 
     let Structural: Int 
     let ConfidenceInManagement: Int 

     // changed from Double 
     let longitude: String 
     let latitude: String 

     // changed to optional 
     let dataSource: String? 
     let returncode: String? 

     // changed from Int 
     let RatingValue: String 

は、以下の変更をモデルクラスを置き換えます

class Scores: Decodable { 
     // set as string because we can expect values such as 'exempt' 
     let Hygiene: Int 
     let Structural: Int 
     let ConfidenceInManagement: Int 

     init(Hygiene: Int, Structural: Int, ConfidenceInManagement: Int) { 
      self.Hygiene = Hygiene 
      self.Structural = Structural 
      self.ConfidenceInManagement = ConfidenceInManagement 
     } 
    } 

    class Geocode: Decodable { 
     let longitude: String 
     let latitude: String 

     init(longitude: String, latitude: String) { 
      self.longitude = longitude 
      self.latitude = latitude 
     } 
    } 

    class Meta: Decodable { 
     let dataSource: String? 
     let extractDate: String 
     let itemCount: Int 
     let returncode: String? 
     let totalCount: Int 
     let totalPages: Int 
     let pageSize: Int 
     let pageNumber: Int 

     init(dataSource: String, extractDate: String, itemCount: Int, returncode: String, totalCount: Int, totalPages: Int, pageSize: Int, pageNumber: Int) { 

      self.dataSource = dataSource 
      self.extractDate = extractDate 
      self.itemCount = itemCount 
      self.returncode = returncode 
      self.totalCount = totalCount 
      self.totalPages = totalPages 
      self.pageSize = pageSize 
      self.pageNumber = pageNumber 
     } 
    } 

    class Establishments: Decodable { 
     let establishments: [Establishment] 

     init(establishments: [Establishment]) { 
      self.establishments = establishments 
     } 
    } 

    class Establishment: Decodable { 
     let FHRSID: Int 
     let LocalAuthorityBusinessID: String 
     let BusinessName: String 
     let BusinessType: String 
     let BusinessTypeID: Int 
     let AddressLine1: String 
     let AddressLine2: String 
     let AddressLine3: String 
     let AddressLine4: String 
     let PostCode: String 
     let Phone: String 
     let RatingValue: String 
     let RatingKey: String 
     let RatingDate: String 
     let LocalAuthorityCode: Int 
     let LocalAuthorityName: String 
     let LocalAuthorityWebSite: String 
     let LocalAuthorityEmailAddress: String 
     let scores: Scores 
     let SchemeType: String 
     let geocode: Geocode 
     let RightToReply: String 
     let Distance: Double 
     let NewRatingPending: Bool 
     let meta: Meta 
     let links: [String] 

     init(FHRSID: Int, LocalAuthorityBusinessID: String, BusinessName: String, BusinessType: String, BusinessTypeID: Int, AddressLine1: String, AddressLine2: String, AddressLine3: String, AddressLine4: String, PostCode: String, Phone: String, RatingValue: String, RatingKey: String, RatingDate: String, LocalAuthorityCode: Int, LocalAuthorityName: String, LocalAuthorityWebSite: String, LocalAuthorityEmailAddress: String, scores: Scores, SchemeType: String, geocode: Geocode, RightToReply: String, Distance: Double, NewRatingPending: Bool, meta: Meta, links: [String]) { 

      self.FHRSID = FHRSID 
      self.LocalAuthorityBusinessID = LocalAuthorityBusinessID 
      self.BusinessName = BusinessName 
      self.BusinessType = BusinessType 
      self.BusinessTypeID = BusinessTypeID 
      self.AddressLine1 = AddressLine1 
      self.AddressLine2 = AddressLine2 
      self.AddressLine3 = AddressLine3 
      self.AddressLine4 = AddressLine4 
      self.PostCode = PostCode 
      self.Phone = Phone 
      self.RatingValue = RatingValue 
      self.RatingKey = RatingKey 
      self.RatingDate = RatingDate 
      self.LocalAuthorityCode = LocalAuthorityCode 
      self.LocalAuthorityName = LocalAuthorityName 
      self.LocalAuthorityWebSite = LocalAuthorityWebSite 
      self.LocalAuthorityEmailAddress = LocalAuthorityEmailAddress 
      self.scores = scores 
      self.SchemeType = SchemeType 
      self.geocode = geocode 
      self.RightToReply = RightToReply 
      self.Distance = Distance 
      self.NewRatingPending = NewRatingPending 
      self.meta = meta 
      self.links = links 
     } 
    } 

は、それが動作します願っています。

+0

ありがとう、これは驚くほど助けになりました。私はこれを以前に試してみたと思っていました(それらをオプションのパラメータとして追加しましたが、明らかに 'null'可能な値のすべてではありません) –

1

このコードは、ヘッダーに正しいコンテンツタイプを設定して、データをJsonとして取得する必要があります。

let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))" 

guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return } 
var request = URLRequest(url: url) 
request.httpMethod = "GET" 
request.addValue("2", forHTTPHeaderField: "x-api-version") 
request.addValue("application/json", forHTTPHeaderField: "accept") 
request.addValue("application/json", forHTTPHeaderField: "content-type") 

URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in 
    guard let data = data, error == nil, response != nil else { 
     print("Something went wrong") 
     return 
    } 

    do { 
    let establishments = try JSONDecoder().decode(Establishments.self, from: data) 
    print(establishments) 
    } catch { 
    print(error) 
    } 
}).resume() 

その後nullすることができ、すべてのプロパティはオプションとして宣言する必要があります。あなたのモデルにどこでもプロパティを小文字にしていることを好む場合、あなたはoverriding the CodingKey enumでキーを再定義することができ、最後の注意として

class Meta: Decodable { 
    let dataSource: String? 
    let extractDate: String 
    let itemCount: Int 
    let returncode: String 
    let totalCount: Int 
    let totalPages: Int 
    let pageSize: Int 
    let pageNumber: Int 
} 

:のみdata-sourceを考慮すると、nullにすることができ、あなたのメタオブジェクトの宣言は次のようになります。

関連する問題