1

I'm having a hard time working out how to parse JSON in Swift. I want to parse out the RouteKey and routeNAME from this xml. I've tried a few different tutorials, and tried to work out quicktype but I couldn't get it to work.

{
"response": {
    "data": [{
        "fieldData": {
            "RouteKey": "AIwACAAAAB4AAABcAAAAmQAAAKYAAAB42mNYxsDOZMfAwMCyjIFhHSMDwy8g24SDgYGJCchg6I39 MjFlkOdAQr2VBQICTFU2jD8\/w8R LCfAQlwAXHqlS5OJoaXd249WvXjtxpMZtuOfCFmhkorPBq5996vADqACS6odOH0dpDNigIMHgwA UQgxkMEKTE=",
            "Route ID": 3.5375656527008e+56,
            "userID": "1",
            "routeNAME": "Zoo to Hartley",
            "Job ID": "",
            "RouteXML": "",
            "length": "",
            "width": "",
            "weight": "",
            "height": "",
            "numtrailers": "",
            "truckID": "1"
        },
        "portalData": [],
        "recordId": "14",
        "modId": "1"
    }, {
        "fieldData": {
            "RouteKey": "ADYBCAAAAB4AAABcAAAAmQAAAJsBAAB42n1QPUtDMRQ9SSMNtmj6dCi6BIVHnbS4KkR4Q10F97e5Kbi5RXmV2lUXcSn0P2g3K3QouIjg1kUnR0EnEetNXipOHrhJ7uc5ubhiRQ6CP 6f7eNsZFYQ0Pk4jw750QbG4zzwdos\/mCa7rC99c2y j16eFr7iSeamdxBp\/Nv4IEbrLPB6DPY r3cksLpM0XR xsUE czXhDqpwU1X9fcBTa7iYClaIiTJrHtUXeCMWxjX2FeIOVHRJLPdtAyiAYXkDqhAppiTUxwl4WdU5W6BJqyJVh2d41OzGNcudLuGyskWQ2aSpPRaJI62wJB oQXx2wxoOHmmQHqIXzvRQNlv1bD8hnAfUE4DgspuLniyfFv 3YQln9lmhh NMTn3hVV0jw==",
            "Route ID": 2.7005919429558e+57,
            "userID": "1",
            "routeNAME": "Perth to Hartley",
            "Job ID": "",
            "RouteXML": "",
            "length": "",
            "width": "",
            "weight": "",
            "height": "",
            "numtrailers": "",
            "truckID": "1"
        },
        "portalData": [],
        "recordId": "19",
        "modId": "1"
    }]
},
"messages": [{
    "code": "0",
    "message": "OK"
}]

}

This is the code I have so far that retrieves the JSON:

let url = URL(string: "https://....")
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!) as? NSDictionary {
print(jsonObj!.value(forKey: "response")!)
}
}).resume()
1
  • 1
    One word: Codable Commented Feb 22, 2019 at 6:35

3 Answers 3

3

If you wan't to make it work with Codable HERE is your file.

And it will look like:

struct Root: Codable {
    let response: Response
    let messages: [Message]
}

struct Message: Codable {
    let code, message: String
}

struct Response: Codable {
    let data: [Datum]
}

struct Datum: Codable {
    let fieldData: FieldData
    let portalData: [JSONAny]
    let recordID, modID: String

    enum CodingKeys: String, CodingKey {
        case fieldData, portalData
        case recordID = "recordId"
        case modID = "modId"
    }
}

struct FieldData: Codable {
    let routeKey: String
    let routeID: Double
    let userID, routeNAME, jobID, routeXML: String
    let length, width, weight, height: String
    let numtrailers, truckID: String

    enum CodingKeys: String, CodingKey {
        case routeKey = "RouteKey"
        case routeID = "Route ID"
        case userID, routeNAME
        case jobID = "Job ID"
        case routeXML = "RouteXML"
        case length, width, weight, height, numtrailers, truckID
    }
}

// MARK: Encode/decode helpers

class JSONNull: Codable, Hashable {

    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
        return true
    }

    public var hashValue: Int {
        return 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()
    }
}

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)
        }
    }
}

Then you can decode JSON with:

let root = try? JSONDecoder().decode(Root.self, from: jsonData!)

and if you want to access data key from your root object you can do it with

root?.response.data

which is An Array of type [Datum]?

Sign up to request clarification or add additional context in comments.

2 Comments

I can output the datum, by print(root?.response.data). How do I now access the routeKey or routeNAME
As I told you it's an Array let me show you one example for routeKey which is root?.response.data[0].fieldData.routeKey
1

The data inside is actually an array. So you can iterate over it and look for the object with desired keys.

if let responseData = response["data"] as? [[String : Any]] {
    for aDataItem in responseData {
        print("routeName :\(aDataItem["routeNmae"]) and routeID:\(aDataItem["Route ID"])")
    }
}

Comments

1

you can parse json using multiple ways Codable, Swift Class, Struct and third party frameworks like SwiftyJSON, Gloss, Struct, Unbox, Maya and many other for more detail CHECK DETAIL LINK

Online tool for generating all kinds of models : jsoncafe.com

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.