Home > Blockchain >  How to decode a JSON array with no name?
How to decode a JSON array with no name?

Time:09-26

I am trying to decode a JSON from URL but my code can not decode this JSON.

This is the json: json

and this is my model object :

struct User: Decodable, Identifiable {
    let name: String
    let birthdate: Date
    let id: Int
}

and this is my fetch users code:

 func fetchUsers() async throws -> [User]? {
        let endPoint = EndPoint.users.rawValue
        guard let url = URL(string: endPoint) else {
            print("Invalid URL")
            return nil
        }
        let urlRequest = URLRequest(url: url)
        do {
            let (json, _) = try await URLSession.shared.data(for: urlRequest)
            return try JSONDecoder().decode([User].self, from: json)
         } catch {
             print(error)
         }
    }
    // create user with async/await
}

when I debug I get this data result of URLSession: json data

CodePudding user response:

The issue there is in your JSONDecoder dateDecodingStrategy. The default strategy is called deferredToDate which means it is expecting the timeIntervalSinceReferenceDate. What you need is a custom dateFormat to parse your ISO8601 date format. Check this How to convert a date string with optional fractional seconds using Codable in Swift?.

Just create a custom DateFormatter iso8601 with and without fractional seconds:

extension Formatter {
    static let iso8601withFractionalSeconds: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
        return formatter
    }()
    static let iso8601: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
        return formatter
    }()
}

And a custom dateDecodingStrategy:

extension JSONDecoder.DateDecodingStrategy {
    static let customISO8601 = custom {
        let container = try $0.singleValueContainer()
        let string = try container.decode(String.self)
        if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) {
            return date
        }
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
    }
}

Now you can set your JSONDecoder dateDecodingStrategy properly:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .customISO8601

And use that decoder to parse your JSON string. Note that if you use try? you are discarding the error which you should use to debug your issue:

    return try decoder.decode([User].self, from: json) 
} catch {
    print(error)
}
  • Related