简体   繁体   中英

Swift Decodable parse part of JSON

I'm trying to parse a json that looks like this using decodable:

    {
    "count": 1,
    "results": [
        {
            "title": 1,
            "content": "Bla"
        } ]
    }

My problem is that I don't want to make a class that has a count property just to be able to use the decoder. I want to parse only the results part I don't care about the count.

So my question is, can decodable.decode somehow only parse a part of the result json. I mean a certain key path instead of the whole json? And I want to do it using Decodable.

In a nutshell I don't want this:

class IncidentWrapper: Codable{
    var count: Int
    var incident: [Incident]
}

What I would Imagine is to have this:

decodable.decode([Incident].self, from: response.data, forKey: "results")

Thanks

let me see what I can suggest:

struct Result: Codeable {
   var id: Int
   var message: String
   var color: String
   var type: String

   enum CodingKeys: String, CodingKey {
      case results
   }

   enum NestedResultKeys: String, CodingKey {
      case id, message, color, type
   }
}

extension Result: Decodable {
   init(from decoder: Decoder) throws {
      let result = try decoder.container(keyedBy: CodingKeys.self)

      let nestedResult = try result.nestedContainer(keyedBy: NestedResultKeys.self, forKey: .result)
      id = try nestedResult.decode(Int.self, forKey: .id)
      message = try nestedResult.decode(String.self, forKey: .message)
      color = try nestedResult.decode(String.self, forKey: .color)
      type = try nestedResult.decode(String.self, forKey: .id)
   }
}

See this documentation for more insight

https://developer.apple.com/documentation/swift/swift_standard_library/encoding_decoding_and_serialization

Hope it helps your project!

You probably is looking for JSONSerialization class. This is an example how it works:

    if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
        if let results = json["results"] as? [Incident] {
            print(results.count)
        }
    }

You can define a generic wrapper for once and use everywhere.

It will work as a generic wrapper for results key only.

protocol ResultsDecodable: Decodable{
    associatedtype T: Decodable
    var results: [T] {get}
}

struct Result<Element: Decodable>: ResultsDecodable{
    typealias T = Element
    var results: [Element]
}

Extend JSONDecoder to get results output.

  extension JSONDecoder {
        func resultDecode<T>(_ type: Result<T>.Type, from data: Data) throws -> [T] where T : Decodable{
            let model = try! decode(type, from: data)
            return model.results
        }
    }

And use like this

var str = #"{"count": 1,"results": [{"title": 1,"content": "Bla"}, {"title": 2,"content": "Bla"} ]}"#

class Incident: Decodable{
    let title: Int
    let content: String
}

let indicents = (try! JSONDecoder().resultDecode(Result<Incident>.self, from: str.data(using: .utf8)!))

See how it makes everything more complex. BETTER USE IncidentWrapper!!!

You only need to use the keys you care about. Just leave off the count. Don't make it part of your struct.

You will only get errors if you can't find a key in the json that you are expecting in the struct. You can avoid this too, though, if you make it an optional in the struct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM