ios - 解码json时出错

标签 ios json swift decode

我尝试使用下面的代码解码 json

请求是:https://public-api.nazk.gov.ua/v1/declaration/4647cd5d-5877-4606-8e61-5ac5869b71e0

struct DeclarationInfoElement: Codable {
let data: DataClass
}

struct DataClass: Codable {
let step0: Step0
let step1: Step1
}

struct Step0: Codable {
let declarationType, declarationYear1: String
}

struct Step1: Codable {
let firstname, lastname, workPost, workPlace: String
}

func fetchDeclarationDetails (with declarationID: String, completion: @escaping(DeclarationInfoElement?) -> Void) {
    var hostURL = "https://public-api.nazk.gov.ua/v1/declaration/"
    hostURL = hostURL + declarationID
    let url = URL(string: hostURL)!

    let dataTask = URLSession.shared.dataTask(with: url) {
        (data, response, error) in
        let jsonDecoder = JSONDecoder()
        print("Trying to decode data...")

        guard let data = data else { return }

        do {
            let declarationInfoElement = try jsonDecoder.decode(DeclarationInfoElement.self, from: data)
            completion(declarationInfoElement)
            print("Done successfully")
        } catch {
            print(error)
        }
        completion(nil)
    }
    dataTask.resume()
}

Json 是:

{"id":"4647cd5d-5877-4606-8e61-5ac5869b71e0","created_date":"05.07.2018","lastmodified_date":"05.07.2018","data":{"step_0":{"declarationType":"2","declarationYearTo":"04.07.2018","declarationYearFrom":"01.01.2018"},"step_1":{"city":"[\u041a\u043e\u043d\u0444\u0456\u0434\u0435\u043d\u0446\u0456\u0439\u043d\u0430 \u0456\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0456\u044f]","region":"[\u041a\u043e\u043d\u0444\u0456\u0434\u0435\u043d\u0446\u0456\u0439\u043d\u0430 ...

..."step_16":{"empty":"\u0423 \u0441\u0443\u0431'\u0454\u043a\u0442\u0430 \u0434\u0435\u043a\u043b\u0430\u0440\u0443\u0432\u0430\u043d\u043d\u044f \u0432\u0456\u0434\u0441\u0443\u0442\u043d\u0456 \u043e\u0431'\u0454\u043a\u0442\u0438 \u0434\u043b\u044f \u0434\u0435\u043a\u043b\u0430\u0440\u0443\u0432\u0430\u043d\u043d\u044f \u0432 \u0446\u044c\u043e\u043c\u0443 \u0440\u043e\u0437\u0434\u0456\u043b\u0456."}}}

我在 JSON Validator 中检查了 Json,它是有效的

但是我得到了错误:

正在尝试解码数据... dataCorrupted(Swift.DecodingError.Context(codingPath:[],debugDescription:“给定的数据不是有效的 JSON。”,underlyingError:可选(Error Domain=NSCocoaErrorDomain Code=3840“JSON 文本未以数组或对象开头,并且选项允许未设置片段。” UserInfo={NSDebugDescription=JSON 文本未以数组或对象开头,且选项未设置允许片段。}))) 2712 字节

找不到答案。请帮忙

最佳答案

在这里,我使用Alamofire为您创建了简单的示例这是应用程序中 API 调用的良好实践。

我已经根据 hereJSON 响应为您创建了 Codable struct .

如下所示:

import Foundation

struct DeclarationInfoElement: Codable {
    let id, createdDate, lastmodifiedDate: String
    let data: DataClass

    enum CodingKeys: String, CodingKey {
        case id
        case createdDate = "created_date"
        case lastmodifiedDate = "lastmodified_date"
        case data
    }
}

struct DataClass: Codable {
    let step0: Step0
    let step1: Step1
    let step2: [JSONAny]
    let step3, step4, step5, step6: Step
    let step7, step8, step9, step10: Step
    let step11: Step11
    let step12, step13, step14, step15: Step
    let step16: Step

    enum CodingKeys: String, CodingKey {
        case step0 = "step_0"
        case step1 = "step_1"
        case step2 = "step_2"
        case step3 = "step_3"
        case step4 = "step_4"
        case step5 = "step_5"
        case step6 = "step_6"
        case step7 = "step_7"
        case step8 = "step_8"
        case step9 = "step_9"
        case step10 = "step_10"
        case step11 = "step_11"
        case step12 = "step_12"
        case step13 = "step_13"
        case step14 = "step_14"
        case step15 = "step_15"
        case step16 = "step_16"
    }
}

struct Step0: Codable {
    let declarationType, declarationYearTo, declarationYearFrom: String
}

struct Step1: Codable {
    let city, region, street, country: String
    let cityPath, cityType, district, lastname: String
    let postCode, postType, workPost, firstname: String
    let workPlace, middlename, streetType: String
    let changedName: Bool
    let countryPath, engPostCode, postCategory, actualStreet: String
    let actualCountry, actualCityType, actualPostCode, actualStreetType: String
    let engActualAddress, previousLastname, ukrActualAddress, corruptionAffected: String
    let engActualPostCode, previousFirstname, previousMiddlename, responsiblePosition: String
    let sameRegLivingAddress, postTypeExtendedstatus, engSameRegLivingAddress, housePartNumExtendedstatus: String
    let postCategoryExtendedstatus, apartmentsNumExtendedstatus: String

    enum CodingKeys: String, CodingKey {
        case city, region, street, country, cityPath, cityType, district, lastname, postCode, postType, workPost, firstname, workPlace, middlename, streetType, changedName, countryPath
        case engPostCode = "eng_postCode"
        case postCategory
        case actualStreet = "actual_street"
        case actualCountry = "actual_country"
        case actualCityType = "actual_cityType"
        case actualPostCode = "actual_postCode"
        case actualStreetType = "actual_streetType"
        case engActualAddress = "eng_actualAddress"
        case previousLastname = "previous_lastname"
        case ukrActualAddress = "ukr_actualAddress"
        case corruptionAffected
        case engActualPostCode = "eng_actualPostCode"
        case previousFirstname = "previous_firstname"
        case previousMiddlename = "previous_middlename"
        case responsiblePosition, sameRegLivingAddress
        case postTypeExtendedstatus = "postType_extendedstatus"
        case engSameRegLivingAddress = "eng_sameRegLivingAddress"
        case housePartNumExtendedstatus = "housePartNum_extendedstatus"
        case postCategoryExtendedstatus = "postCategory_extendedstatus"
        case apartmentsNumExtendedstatus = "apartmentsNum_extendedstatus"
    }
}

struct Step: Codable {
    let empty: String
}

struct Step11: Codable {
    let the1530711324488: The1530711324488

    enum CodingKeys: String, CodingKey {
        case the1530711324488 = "1530711324488"
    }
}

struct The1530711324488: Codable {
    let person: String
    let rights: Rights
    let iteration, objectType, sizeIncome, incomeSource: String
    let sourceCitizen, otherObjectType, sourceUaLastname, sourceEngFullname: String
    let sourceUaFirstname, sourceUkrFullname, sourceUaMiddlename, sourceUaCompanyCode: String
    let sourceUaCompanyName, sourceEngCompanyCode, sourceEngCompanyName, sourceUkrCompanyName: String
    let objectTypeExtendedstatus, sizeIncomeExtendedstatus, sourceEngCompanyAddress, sourceUkrCompanyAddress: String
    let incomeSourceExtendedstatus, sourceCitizenExtendedstatus, otherObjectTypeExtendedstatus, sourceUaSameRegLivingAddress: String
    let sourceUaCompanyCodeExtendedstatus, sourceUaCompanyNameExtendedstatus: String

    enum CodingKeys: String, CodingKey {
        case person, rights, iteration, objectType, sizeIncome, incomeSource
        case sourceCitizen = "source_citizen"
        case otherObjectType
        case sourceUaLastname = "source_ua_lastname"
        case sourceEngFullname = "source_eng_fullname"
        case sourceUaFirstname = "source_ua_firstname"
        case sourceUkrFullname = "source_ukr_fullname"
        case sourceUaMiddlename = "source_ua_middlename"
        case sourceUaCompanyCode = "source_ua_company_code"
        case sourceUaCompanyName = "source_ua_company_name"
        case sourceEngCompanyCode = "source_eng_company_code"
        case sourceEngCompanyName = "source_eng_company_name"
        case sourceUkrCompanyName = "source_ukr_company_name"
        case objectTypeExtendedstatus = "objectType_extendedstatus"
        case sizeIncomeExtendedstatus = "sizeIncome_extendedstatus"
        case sourceEngCompanyAddress = "source_eng_company_address"
        case sourceUkrCompanyAddress = "source_ukr_company_address"
        case incomeSourceExtendedstatus = "incomeSource_extendedstatus"
        case sourceCitizenExtendedstatus = "source_citizen_extendedstatus"
        case otherObjectTypeExtendedstatus = "otherObjectType_extendedstatus"
        case sourceUaSameRegLivingAddress = "source_ua_sameRegLivingAddress"
        case sourceUaCompanyCodeExtendedstatus = "source_ua_company_code_extendedstatus"
        case sourceUaCompanyNameExtendedstatus = "source_ua_company_name_extendedstatus"
    }
}

struct Rights: Codable {
    let the1: [String: String]

    enum CodingKeys: String, CodingKey {
        case the1 = "1"
    }
}

// MARK: Encode/decode helpers

class JSONNull: Codable {
    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()
    }
}

class JSONCodingKey: CodingKey {
    let key: String

    required init?(intValue: Int) {
        return nil
    }

    required init?(stringValue: String) {
        key = stringValue
    }

    var intValue: Int? {
        return nil
    }

    var stringValue: String {
        return key
    }
}

class JSONAny: Codable {
    let value: Any

    static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
        let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
        return DecodingError.typeMismatch(JSONAny.self, context)
    }

    static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
        let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
        return EncodingError.invalidValue(value, context)
    }

    static func decode(from container: SingleValueDecodingContainer) throws -> Any {
        if let value = try? container.decode(Bool.self) {
            return value
        }
        if let value = try? container.decode(Int64.self) {
            return value
        }
        if let value = try? container.decode(Double.self) {
            return value
        }
        if let value = try? container.decode(String.self) {
            return value
        }
        if container.decodeNil() {
            return JSONNull()
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
        if let value = try? container.decode(Bool.self) {
            return value
        }
        if let value = try? container.decode(Int64.self) {
            return value
        }
        if let value = try? container.decode(Double.self) {
            return value
        }
        if let value = try? container.decode(String.self) {
            return value
        }
        if let value = try? container.decodeNil() {
            if value {
                return JSONNull()
            }
        }
        if var container = try? container.nestedUnkeyedContainer() {
            return try decodeArray(from: &container)
        }
        if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
            return try decodeDictionary(from: &container)
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
        if let value = try? container.decode(Bool.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(Int64.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(Double.self, forKey: key) {
            return value
        }
        if let value = try? container.decode(String.self, forKey: key) {
            return value
        }
        if let value = try? container.decodeNil(forKey: key) {
            if value {
                return JSONNull()
            }
        }
        if var container = try? container.nestedUnkeyedContainer(forKey: key) {
            return try decodeArray(from: &container)
        }
        if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
            return try decodeDictionary(from: &container)
        }
        throw decodingError(forCodingPath: container.codingPath)
    }

    static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
        var arr: [Any] = []
        while !container.isAtEnd {
            let value = try decode(from: &container)
            arr.append(value)
        }
        return arr
    }

    static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
        var dict = [String: Any]()
        for key in container.allKeys {
            let value = try decode(from: &container, forKey: key)
            dict[key.stringValue] = value
        }
        return dict
    }

    static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
        for value in array {
            if let value = value as? Bool {
                try container.encode(value)
            } else if let value = value as? Int64 {
                try container.encode(value)
            } else if let value = value as? Double {
                try container.encode(value)
            } else if let value = value as? String {
                try container.encode(value)
            } else if value is JSONNull {
                try container.encodeNil()
            } else if let value = value as? [Any] {
                var container = container.nestedUnkeyedContainer()
                try encode(to: &container, array: value)
            } else if let value = value as? [String: Any] {
                var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
                try encode(to: &container, dictionary: value)
            } else {
                throw encodingError(forValue: value, codingPath: container.codingPath)
            }
        }
    }

    static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
        for (key, value) in dictionary {
            let key = JSONCodingKey(stringValue: key)!
            if let value = value as? Bool {
                try container.encode(value, forKey: key)
            } else if let value = value as? Int64 {
                try container.encode(value, forKey: key)
            } else if let value = value as? Double {
                try container.encode(value, forKey: key)
            } else if let value = value as? String {
                try container.encode(value, forKey: key)
            } else if value is JSONNull {
                try container.encodeNil(forKey: key)
            } else if let value = value as? [Any] {
                var container = container.nestedUnkeyedContainer(forKey: key)
                try encode(to: &container, array: value)
            } else if let value = value as? [String: Any] {
                var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
                try encode(to: &container, dictionary: value)
            } else {
                throw encodingError(forValue: value, codingPath: container.codingPath)
            }
        }
    }

    static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
        if let value = value as? Bool {
            try container.encode(value)
        } else if let value = value as? Int64 {
            try container.encode(value)
        } else if let value = value as? Double {
            try container.encode(value)
        } else if let value = value as? String {
            try container.encode(value)
        } else if value is JSONNull {
            try container.encodeNil()
        } else {
            throw encodingError(forValue: value, codingPath: container.codingPath)
        }
    }

    public required init(from decoder: Decoder) throws {
        if var arrayContainer = try? decoder.unkeyedContainer() {
            self.value = try JSONAny.decodeArray(from: &arrayContainer)
        } else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
            self.value = try JSONAny.decodeDictionary(from: &container)
        } else {
            let container = try decoder.singleValueContainer()
            self.value = try JSONAny.decode(from: container)
        }
    }

    public func encode(to encoder: Encoder) throws {
        if let arr = self.value as? [Any] {
            var container = encoder.unkeyedContainer()
            try JSONAny.encode(to: &container, array: arr)
        } else if let dict = self.value as? [String: Any] {
            var container = encoder.container(keyedBy: JSONCodingKey.self)
            try JSONAny.encode(to: &container, dictionary: dict)
        } else {
            var container = encoder.singleValueContainer()
            try JSONAny.encode(to: &container, value: self.value)
        }
    }
}

您的 API 调用将是:

Alamofire.request("https://public-api.nazk.gov.ua/v1/declaration/4647cd5d-5877-4606-8e61-5ac5869b71e0").responseData { response in
        switch response.result {
        case .success(let data):
            let declarationInfoElement = try? JSONDecoder().decode(DeclarationInfoElement.self, from: data)
            print(declarationInfoElement)
        case .failure:
            print("fail")
        }
    }

here是演示项目。希望这会有所帮助。

关于ios - 解码json时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51518829/

相关文章:

ios - 在没有动画的情况下呈现 UIViewController 会显示演示者 UIViewController 的时间

python - tornado 将 GET 和 POST 参数映射到列表。如何禁用此 "feature"?

swift - 委托(delegate)与类引用

iOS - 单击按钮隐藏状态栏

ios - 在两个 PFObject 上存储一对一指针

json - 给定 JSON (NoSQL) 中所有记录的特定字段的打印值

Swift CloudKit QueryOperation 重复

swift - locationManager 未被识别为类变量

ios - 在初始 View Controller 实例化时以编程方式绘制 ImageView 边界

java - 使用 JSON 和 AJAX 发送和检索数据