Decoding JSON array of different types in Swift

后端 未结 3 862
夕颜
夕颜 2020-12-18 12:36

I\'m trying to decode the following JSON Object

{
    \"result\":[
             {
             \"rank\":12,
             \"user\":{ 
                     \"n         


        
相关标签:
3条回答
  • 2020-12-18 13:25

    If your JSON format is given then you are pretty much out of luck, since you will most likely have to parse your array as [Any] which is, to put it mildly, not very useful. If on the other hand you are able to modify the format of the JSON you should start from the other direction. Define your desired Swift object and encode it using JSONEncoder.encode(...) in order to quickly determine how your JSON should look like in order to make it parse in as typed a way as possible.

    This approach will easily half your JSON handling code as your web service protocol will end up being structured much better. This will likely improve the structure of the overall system since it will yield a much more stable communication protocol.

    Sadly enough this approach is not always possible which is when things get messy. Given your example you will be able to parse your code as

    let st = """
    {
        "result":[
            {
                "rank":12,
                "user":{
                    "name":"bob",
                    "age":12
                }
            },
            {
                "1":[
                    {
                        "name":"bob","age":12
                    },
                    {
                        "name":"tim","age":13
                    },
                    {
                        "name":"tony","age":12
                    },
                    {
                        "name":"greg","age":13
                    }
                ]
            }
        ]
    }
    """
    
    let jsonData1 = st.data(using: .utf8)!
    let arbitrary = try JSONSerialization.jsonObject(with: jsonData1, options: .mutableContainers)
    

    This will let you access your data with a bunch of casts as in

    let dict = arbitrary as! NSDictionary
    print(dict["result"])
    

    you get the idea. not very useful as you would very much like to use the Codable protocol as in

     struct ArrayRes : Codable {
         let result : [[String:Any]]
     }
    
     let decoder1 = JSONDecoder()
     do {
         let addrRes = try decoder.decode(ArrayRes.self, from: jsonData1)
            print(addrRes)
         } catch {
            print("error on decode: \(error.localizedDescription)")
     }
    

    Unfortunately this does not work since Any is not Codable for slightly obvious reasons.

    I hope you are able to change your JSON protocol since the current one will be the root cause of lot of messy code.

    0 讨论(0)
  • 2020-12-18 13:30

    You have to deserialise the Json like this

    json = try JSONSerialization.jsonObject(with: data!) as? [Any]
    

    and after that you need to show the program how to set the data up like this.

    guard let item = json! as? [String: Any],
      var rank = item["..."] as! whatever kind of variable you whant else {                     
    
      return 
    }
    

    and then depending on how many jsonobjects you have you should loop through them and when you have done this you should send the item to a func that puts the values in your own class if you whant them in a object. ps when you are showing the program how to setup the Json object you can go through the item object as a dictionary as i am showing you in the second codebit.

    0 讨论(0)
  • 2020-12-18 13:41

    Just for fun:

    First you need structs for the users and the representation of the first and second dictionary in the result array. The key "1" is mapped to one

    struct User : Decodable {
        let name : String
        let age : Int
    }
    
    struct FirstDictionary : Decodable {
        let rank : Int
        let user : User
    }
    
    struct SecondDictionary : Decodable {
        let one : [User]
    
        private enum CodingKeys: String, CodingKey { case one = "1" }
    }
    

    Now comes the tricky part:

    • First get the root container.
    • Get the container for result as nestedUnkeyedContainer because the object is an array.
    • Decode the first dictionary and copy the values.
    • Decode the second dictionary and copy the values.

      struct UserData: Decodable {
      
          let rank : Int
          let user : User
          let oneUsers : [User]
      
         private enum CodingKeys: String, CodingKey { case result }
      
          init(from decoder: Decoder) throws {
              let container = try decoder.container(keyedBy: CodingKeys.self)
              var arrayContainer = try container.nestedUnkeyedContainer(forKey: .result)
              let firstDictionary = try arrayContainer.decode(FirstDictionary.self)
              rank = firstDictionary.rank
              user = firstDictionary.user
              let secondDictionary = try arrayContainer.decode(SecondDictionary.self)
              oneUsers = secondDictionary.one
          }
      }
      

    If this code is preferable over traditional manual JSONSerialization is another question.

    0 讨论(0)
提交回复
热议问题