So, the issue is - I am trying to display the Mars weather from the Mars Insight API. Here is a link Insight Weather, the data is returning in JSON format and has three levels. The keys have names that change depending on the current date (sols). How to make the structure of mutable properties?... when the property names change every day. Do we have any instruments to parse such JSON?
{
"815": {
"First_UTC": "2021-03-12T14:54:38Z",
"Last_UTC": "2021-03-13T15:34:09Z",
"Month_ordinal": 12,
"Northern_season": "late winter",
"PRE": {
"av": 728.378,
"ct": 153082,
"mn": 708.4211,
"mx": 744.9279
},
"Season": "winter",
"Southern_season": "late summer",
"WD": {
"most_common": null
}
},
"818": {
"First_UTC": "2021-03-15T20:01:49Z",
"Last_UTC": "2021-03-16T17:32:54Z",
"Month_ordinal": 12,
"Northern_season": "late winter",
"PRE": {
"av": 727.696,
"ct": 109855,
"mn": 710.223,
"mx": 743.946
},
"Season": "winter",
"Southern_season": "late summer",
"WD": {
"most_common": null
}
},
"819": {
....
,
"sol_keys": [
"815",
"818",
"819",
"820",
"821"
],
"validity_checks": {
"815": {
"PRE": {
"sol_hours_with_data": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23
],
"valid": true
}
}
So, Structure:
import Foundation
struct WeatherData: Decodable {
let season: String?
let pre: pre?
let solKeys: [String]
enum CodingKeys: String, CodingKey {
case season = "season"
case pre = "PRE"
case solKeys = "sol_keys"
}
struct pre: Decodable {
let av: Double?
let mn: Double?
let mx: Double?
enum CodingKeys: String, CodingKey {
case av = "av"
case mn = "mn"
case mx = "mx"
}
}
}
Parsing:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://api.nasa.gov/insight_weather/?api_key=DEMO_KEY&feedtype=json&ver=1.0"
guard let url = URL(string: urlString) else { return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let safeData = data else { return }
do {
let solWeather = try JSONDecoder().decode([String:WeatherData].self, from: safeData)
if let keys = solWeather["sol_keys"] {
for key in keys { //error: For-in loop requires 'WeatherData' to conform to 'Sequence'
let report = solWeather [key]
}
}
print(solWeather)
}
catch let error {
print(error)
}
}.resume()
}
}
Got an error: For-in loop requires 'WeatherData' to conform to 'Sequence'
Don't understand why :(( Can someone help me?
Thanks!
You cannot decode [String:WeatherData].self because the dictionary contains other values which are not WeatherData, for example the [String] value of sol_keys.
The only way to decode this JSON with JSONDecoder is to implement init(with decoder, decode the sol_keys and create your own temporary CodingKeys to be able to decode the arbitrary dictionary keys.
First declare the custom CodingKey
public struct SolKeys: CodingKey {
public let stringValue: String
public init?(stringValue: String) { self.stringValue = stringValue }
public var intValue: Int? { return nil }
public init?(intValue: Int) { return nil }
}
The Decodable structs are
struct SolData : Decodable {
let firstUTC, lastUTC : Date
let pre : Pre
private enum CodingKeys : String, CodingKey {
case firstUTC = "First_UTC", lastUTC = "Last_UTC", pre = "PRE"
}
}
struct Pre: Decodable {
let av, mn, mx : Double
}
struct WeatherData: Decodable {
let solKeys: [String]
var soldata = [String:SolData]()
enum CodingKeys: String, CodingKey {
case solKeys = "sol_keys"
}
init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.solKeys = try container.decode([String].self, forKey: .solKeys)
let customContainer = try decoder.container(keyedBy: SolKeys.self)
for key in solKeys {
let solKey = SolKeys(stringValue: key)!
let data = try customContainer.decode(SolData.self, forKey: solKey)
soldata[key] = data
}
}
}
And the code to receive and decode the data
let urlString = "https://api.nasa.gov/insight_weather/?api_key=DEMO_KEY&feedtype=json&ver=1.0"
let url = URL(string: urlString)!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let solWeather = try decoder.decode(WeatherData.self, from: data!)
let keys = solWeather.solKeys
for key in keys {
let report = solWeather.soldata[key]!
print(report)
}
}
catch {
print(error)
}
}.resume()
Related
I am running into an issue building the correct data model for the following JSON response.
{
"resources": [
{
"courseid": 4803,
"color": "Blue",
"teeboxtype": "Championship",
"slope": 121,
"rating": 71.4
},
{
"courseid": 4803,
"color": "White",
"teeboxtype": "Men's",
"slope": 120,
"rating": 69.6
},
{
"courseid": 4803,
"color": "Red",
"teeboxtype": "Women's",
"slope": 118,
"rating": 71.2
}
]
}
Here is the current model. No matter what I do I can't seem to get the model populated. Here is also my URL session retrieving the data. I am new to Swift and SwiftUI so please be gentle. I am getting data back however I am missing something.
import Foundation
struct RatingsResources: Codable {
let golfcourserating : [GolfCourseRating]?
}
struct GolfCourseRating: Codable {
let id: UUID = UUID()
let courseID: Int?
let teeColor: String?
let teeboxtype: String?
let teeslope: Double?
let teerating: Double?
enum CodingKeysRatings: String, CodingKey {
case courseID = "courseid"
case teeColor = "color"
case teeboxtype
case teeslope = "slope"
case teerating = "rating"
}
}
func getCoureRating(courseID: String?) {
let semaphore = DispatchSemaphore (value: 0)
print("GETTING COURSE TEE RATINGS..........")
let urlString: String = "https://api.golfbert.com/v1/courses/\(courseID ?? "4800")/teeboxes"
print ("API STRING: \(urlString) ")
let url = URLComponents(string: urlString)!
let request = URLRequest(url: url.url!).signed
let task = URLSession.shared.dataTask(with: request) { data, response, error in
let decoder = JSONDecoder()
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
if let response = try? JSONDecoder().decode([RatingsResources].self, from: data) {
DispatchQueue.main.async {
self.ratingresources = response
}
return
}
print("*******Data String***********")
print(String(data: data, encoding: .utf8)!)
print("***************************")
let ratingsData: RatingsResources = try! decoder.decode(RatingsResources.self, from: data)
print("Resources count \(ratingsData.golfcourserating?.count)")
semaphore.signal()
}
task.resume()
semaphore.wait()
} //: END OF GET COURSE SCORECARD
First of all, never use try? while decoding your JSON. This will hide all errors from you. Use try and an appropriate do/catch block. In the catch block at least print the error.
Looking at your model there seem to be three issues here.
You don´t have an array of RatingsResources in your array. It is just a single instance.
let response = try JSONDecoder().decode(RatingsResources.self, from: data)
RatingsResources is not implemented correct.
let golfcourserating : [GolfCourseRating]?
should be:
let resources: [GolfCourseRating]?
Your coding keys are implemented wrong instead of:
enum CodingKeysRatings: String, CodingKey {
it should read:
enum CodingKeys: String, CodingKey {
You should add enum CodingKey with resources at struct RatingsResources
And decode:
if let response = try? JSONDecoder().decode(RatingsResources.self, from: data) {
// Your response handler
}
This is my first time working with JSON in Swift and when i'm trying to parse the file with my model, this error appears:
The given data was not valid JSON.
I think the problem lies with how I do my model.
The Way I parse the JSON:
import SwiftUI
struct EmergencyView: View {
let emergency: [EmergencyNumberModel]
init() {
let url = Bundle.main.url(forResource: "emergency",
withExtension: "json")!
let data = try! Data(contentsOf: url)
emergency = try! JSONDecoder().decode([EmergencyNumberModel].self, from: data) //Error
}
var body: some View {
List(emergency, id: \.id) { emer in
if emer.Country != nil {
Label(emer.Country, systemImage: "quote.bubble")
.font(.headline)
} else{
Text(emer.Country)
}
}
navigationTitle("Emergency")
}
}
This is a fraction of the JSON i'm using, "emergency.json":
[
{
"Country": {
"Name": "Afghanistan",
"ISOCode": "AF",
"ISONumeric": "4"
},
"Ambulance": {
"All": [
"112"
]
},
"Fire": {
"All": [
"119"
]
},
"Police": {
"All": [
"119"
]
},
"Dispatch": {
"All": [
null
]
},
"Member_112": false,
"LocalOnly": true,
"Notes": false
},
.
.
.
]
This is my Model, "EmergencyNumberModel.swift":
struct EmergencyNumberModel: Codable, Identifiable {
var id = UUID()
let Country: String
let Ambulance: String
let Fire: String
let Police: String
let Dispatch: String
}
Do I need other variables in my model to access the inner keys or the data types of the variables are wrong?
Using https://app.quicktype.io/, to generate the swift strutures,
this is one basic approach to use your json data:
struct EmergencyView: View {
#State var emergencies: [EmergencyModel] = [] // <--- here
var body: some View {
List(emergencies) { emer in
if emer.country.name.isEmpty {
Text("no country name")
} else {
Label(emer.country.name, systemImage: "quote.bubble").font(.headline)
}
}
.onAppear {
if let emrgncy = loadData(from: "emergency") { // <--- here
emergencies = emrgncy
}
}
}
func loadData(from file: String) -> [EmergencyModel]? {
do {
if let filePath = Bundle.main.path(forResource: file, ofType: "json") {
let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
let results = try JSONDecoder().decode([EmergencyModel].self, from: data)
return results
}
} catch {
print("----> error: \(error)") // <-- todo, deal with errors
}
return nil
}
}
struct EmergencyModel: Identifiable, Codable {
let id = UUID() // <--- here
let country: Country
let ambulance, fire, police: Ambulance
let dispatch: Dispatch
let member112, localOnly, notes: Bool
enum CodingKeys: String, CodingKey {
case country = "Country"
case ambulance = "Ambulance"
case fire = "Fire"
case police = "Police"
case dispatch = "Dispatch"
case member112 = "Member_112"
case localOnly = "LocalOnly"
case notes = "Notes"
}
}
struct Ambulance: Codable {
let all: [String]
enum CodingKeys: String, CodingKey {
case all = "All"
}
}
struct Country: Codable {
let name, isoCode, isoNumeric: String
enum CodingKeys: String, CodingKey {
case name = "Name"
case isoCode = "ISOCode"
case isoNumeric = "ISONumeric"
}
}
struct Dispatch: Codable {
let all: [JSONNull?]
enum CodingKeys: String, CodingKey {
case all = "All"
}
}
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
func hash(into hasher: inout Hasher) {
hasher.combine(0)
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
Generic version:
/// Reads a JSON file into a Model object of type T
class JsonReader<T> where T: Decodable {
static func loadData(from file: String) -> T? {
do {
if let filePath = Bundle.main.path(forResource: file, ofType: "json") {
let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
let results = try JSONDecoder().decode(T.self, from: data)
return results
}
} catch {
print("Error: \(error)")
}
return nil
}
}
How to use:
let model = JsonReader<Model>.loadData(from: "FileName")!
I have a requirement to encode/decode snakeCased JSONs. I found that encoder encodes Value2 object correctly, however decoder fails to decode it. What I do wrong here?
Required Json format:
{
"address_line_1" : "Address",
"full_name" : "Name",
"id" : 2
}
Code:
struct Value1: Codable {
let id: Int
let fullName: String
let addressLine1: String
}
struct Value2: Codable {
let id: Int
let fullName: String
let addressLine_1: String
}
func printJson(_ object: Data) throws {
let json = try JSONSerialization.jsonObject(with: object, options: [])
let data = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted, .sortedKeys])
print(String(data: data, encoding: .utf8)!)
}
func encode<T: Encodable>(_ object: T) throws -> Data {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
return try encoder.encode(object)
}
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
_ = try decoder.decode(type, from: data)
print("✅ Decoded \(type) from:")
try printJson(data)
}
do {
var data: Data
data = try encode(Value1(id: 1, fullName: "Name", addressLine1: "Address"))
try decode(Value1.self, from: data)
data = try encode(Value2(id: 2, fullName: "Name", addressLine_1: "Address"))
_ = try decode(Value1.self, from: data)
_ = try decode(Value2.self, from: data)
} catch {
print("❌ Failed with error:", error)
}
Output:
✅ Decoded Value1 from:
{
"address_line1" : "Address",
"full_name" : "Name",
"id" : 1
}
✅ Decoded Value1 from:
{
"address_line_1" : "Address",
"full_name" : "Name",
"id" : 2
}
❌ Failed with error: keyNotFound(CodingKeys(stringValue: "addressLine_1", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"addressLine_1\", intValue: nil) (\"addressLine_1\"), with divergent representation addressLine1, converted to address_line_1.", underlyingError: nil))
convertFromSnakeCase works correctly and you can can check it in first decode:
_ = try decode(Value1.self, from: data)
After that, when you try to decode the same data but with Value2 type it surely fails as it expects different property name. This is your encoded snake case JSON:
{
"address_line_1" : "Address",
"full_name" : "Name",
"id" : 2
}
After decoder conversion address_line_1 becomes addressLine1 (the same applies to full_name) which fits properties of Value1. If you try to decode the same data for Value2 it fails as property name requires addressLine_1.
In your case, optimal strategy would be to use custom coding keys, like this:
struct Value2: Codable {
private enum Value2CodingKey: String, CodingKey {
case id
case fullName = "full_name"
case addressLine1 = "address_line_1"
}
let id: Int
let fullName: String
let addressLine1: String
}
I found a solution without using custom coding keys, but custom coding strategy instead, so coders handle _ before numbers as well.
So that addressLine1 encodes to address_line_1, and address_line_1 decodes to addressLine1
Usage:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCaseWithNumbers
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCaseWithNumbers
Coder implementation:
extension JSONEncoder.KeyEncodingStrategy {
static var convertToSnakeCaseWithNumbers: JSONEncoder.KeyEncodingStrategy {
.custom { codingKeys -> CodingKey in
let stringValue = codingKeys.last!.stringValue
let newKey = AnyKey(stringValue: convertToSnakeCase(stringValue))!
return newKey
}
}
private static func convertToSnakeCase(_ stringKey: String) -> String {
var key = stringKey
let searchRange = key.index(after: key.startIndex)..<key.endIndex
let nsRange = key.nsRange(from: searchRange)
let matches = NSRegularExpression("([A-Z])|([0-9]+)").matches(in: key, options: [], range: nsRange)
for match in matches.reversed() {
guard let range = key.range(from: match.range) else { continue }
key.insert("_", at: range.lowerBound)
}
return key.lowercased()
}
}
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromSnakeCaseWithNumbers: JSONDecoder.KeyDecodingStrategy {
.custom { (codingKeys) -> CodingKey in
let stringValue = codingKeys.last!.stringValue
let newKey = AnyKey(stringValue: convertFromSnakeCase(stringValue))!
return newKey
}
}
private static func convertFromSnakeCase(_ stringKey: String) -> String {
guard stringKey.contains("_") else {
return stringKey
}
let components = stringKey.split(separator: "_").map({ $0.firstCapitalized })
return components.joined().firstLowercased
}
}
private extension NSRegularExpression {
convenience init(_ pattern: String) {
do {
try self.init(pattern: pattern)
} catch {
preconditionFailure("Illegal regular expression: \(pattern).")
}
}
}
private extension StringProtocol {
var firstLowercased: String { prefix(1).lowercased() + dropFirst() }
var firstCapitalized: String { prefix(1).capitalized + dropFirst() }
}
enum AnyKey: CodingKey {
case string(String)
case int(Int)
var stringValue: String {
switch self {
case .string(let string):
return string
case .int(let int):
return "\(int)"
}
}
var intValue: Int? {
guard case let .int(int) = self else { return nil }
return int
}
init?(stringValue: String) {
guard !stringValue.isEmpty else { return nil }
self = .string(stringValue)
}
init?(intValue: Int) {
self = .int(intValue)
}
}
This is the code I am using but I am unable to fetch the JSON.
Error message:
Expected to decode Dictionary<String, Any> but found an array instead.
func getMovieByName(completion: #escaping (Bool, Any?, Error?) -> Void) {
guard let url = URL(string:"https://api.themoviedb.org/3/search/movie?api_key=4cb1eeab94f45affe2536f2c684a5c9e&query='star") else { return }
let session = URLSession.shared
let task = session.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
let items = try JSONDecoder().decode(ItemList.self, from: data)
DispatchQueue.main.async {
completion(true, items, nil)
}
} catch {
DispatchQueue.main.async {
completion(false, nil, error)
}
}
}
task.resume()
}
JSON:
{
"page": 1,
"total_results": 2102,
"total_pages": 106,
"results": [{
"vote_count": 9052,
"id": 11,
"video": false,
"vote_average": 8.2,
"title": "Star Wars",
"popularity": 31.502792,
"poster_path": "/btTdmkgIvOi0FFip1sPuZI2oQG6.jpg",
"original_language": "en",
"original_title": "Star Wars",
"genre_ids": [
12,
28,
878
],
"backdrop_path": "/4iJfYYoQzZcONB9hNzg0J0wWyPH.jpg",
"adult": false,
"overview": "Princess Leia is captured and held hostage by the evil Imperial forces in their effort to take over the galactic Empire. Venturesome Luke Skywalker and dashing captain Han Solo team together with the loveable robot duo R2-D2 and C-3PO to rescue the beautiful princess and restore peace and justice in the Empire.",
"release_date": "1977-05-25"
}]
}
struct Results: Codable {
let id: Int
let title: String
let poster_path: String
struct ItemList: Codable {
let results: Results
}
}
You can create a Swift Struct for this purpose. Here is how you do it.
import Foundation
struct MovieStruct: Codable {
let page, totalResults, totalPages: Int?
let results: [Result]?
enum CodingKeys: String, CodingKey {
case page
case totalResults = "total_results"
case totalPages = "total_pages"
case results
}
}
struct Result: Codable {
let voteCount, id: Int?
let video: Bool?
let voteAverage: Double?
let title: String?
let popularity: Double?
let posterPath, originalLanguage, originalTitle: String?
let genreIDS: [Int]?
let backdropPath: String?
let adult: Bool?
let overview, releaseDate: String?
enum CodingKeys: String, CodingKey {
case voteCount = "vote_count"
case id, video
case voteAverage = "vote_average"
case title, popularity
case posterPath = "poster_path"
case originalLanguage = "original_language"
case originalTitle = "original_title"
case genreIDS = "genre_ids"
case backdropPath = "backdrop_path"
case adult, overview
case releaseDate = "release_date"
}
}
After you have created the struct, you can do something like this to parse your data.
func getMovieByName(completion: #escaping (Bool, Any?, Error?) -> Void) {
guard let url = URL(string:"https://api.themoviedb.org/3/search/movie?api_key=4cb1eeab94f45affe2536f2c684a5c9e&query='star") else { return }
let session = URLSession.shared
let task = session.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
let items = try? JSONDecoder().decode(MovieStruct.self, from: jsonData)
DispatchQueue.main.async {
completion(true, items, nil)
}
} catch {
DispatchQueue.main.async {
completion(false, nil, error)
}
}
}
task.resume()
}
Note I have created the struct with the JSON which you have given. I hope it helps.
func getMovieByName(completion: #escaping (Bool, Any?, Error?) -> Void) {
guard let url = URL(string:"https://api.themoviedb.org/3/search/movie?api_key=4cb1eeab94f45affe2536f2c684a5c9e&query='star") else { return }
let session = URLSession.shared
let task = session.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
let items = try JSONDecoder().decode(Result.self, from: data)
DispatchQueue.main.async {
completion(true, items, nil)
}
} catch {
DispatchQueue.main.async {
completion(false, nil, error)
}
}
}
task.resume()
}
Result struct contains the contents of "results"
struct Result: Codable {
var results: [Movie]
}
Add variables to correspond to the item's fields
struct Movie: Codable {
var id: Int
var vote_count: Int
var title: String
//etc..
}
I got to know struct "Codable" in swift 4.0, *.
So, I tried that when decode josn.
if let jsonData = jsonString.data(using: .utf8) {
let decodingData = try? JSONDecoder().decode(SampleModel.self, from: jsonData)
}
Example sample data model below.
struct SampleModel : Codable {
var no: Int?
var category: Int?
var template_seq: Int?
}
And sample json data is .. below.
{
"data": {
"result" : 1
"total_count": 523,
"list": [
{
"no": 16398,
"category" : 23,
"template_seq" : 1
},
{
"no": -1,
"category" : 23,
"template_seq" : 1
}
]
}
}
But i want filtering wrong data.
If the value of "no" is less than or equal to 0, it is an invalid value.
Before not using codable...below.
(using Alamifre ison response )
guard let dictionaryData = responseJSON as? [String : Any] else { return nil }
guard let resultCode = dictionaryData["result"] as? Bool , resultCode == true else { return nil }
guard let theContainedData = dictionaryData["data"] as? [String:Any] else { return nil }
guard let sampleListData = theContainedData["list"] as? [[String : Any]] else { return nil }
var myListData = [MyEstimateListData]()
for theSample in sampleListData {
guard let existNo = theSample["no"] as? Int, existNo > 0 else {
continue
}
myListData.append( ... )
}
return myListData
how to filter wrong data or invalid data using swift 4.0 Codable ??
you can make codable for inital resonse
Here is your model:
import Foundation
struct Initial: Codable {
let data: DataModel?
}
struct DataModel: Codable {
let result, totalCount: Int
let list: [List]?
enum CodingKeys: String, CodingKey {
case result
case totalCount = "total_count"
case list
}
}
struct List: Codable {
let no, category, templateSeq: Int
enum CodingKeys: String, CodingKey {
case no, category
case templateSeq = "template_seq"
}
}
extension Initial {
init(data: Data) throws {
self = try JSONDecoder().decode(Initial.self, from: data)
}
}
And use it like that :
if let initail = try? Initial.init(data: data) , let list = initail.data?.list {
var myListData = list.filter { $0.no > 0 }
}
Yes, you have to use filters for that with codable:
1: Your struct should be according to your response like that:
struct SampleModel : Codable {
var result: Int?
var total_count: Int?
var list: [List]?
}
struct List : Codable {
var no: Int?
var category: Int?
var template_seq: Int?
}
2: Parse your response using a codable struct like that:
do {
let jsonData = try JSONSerialization.data(withJSONObject: dictionaryData["data"] as Any, options: JSONSerialization.WritingOptions.prettyPrinted)
let resultData = try JSONDecoder().decode(SampleModel.self, from: jsonData)
success(result as AnyObject)
} catch let message {
print("JSON serialization error:" + "\(message)")
}
3: now you can filter invalid data simply:
let filterListData = resultData.list?.filter({$0.no > 0})
let invalidData = resultData.list?.filter({$0.no <= 0})