bignerdranch / Freddy

A reusable framework for parsing JSON in Swift.
MIT License
1.09k stars 119 forks source link

How to decode json with a root array? #138

Closed trispo closed 8 years ago

trispo commented 8 years ago

I'm trying to decode json with a root array containing dictionaries like this:

[
  {
    "identifier": "id 1",
    "name": "name 1",
    "image": "path 1"
  },
  {
    "identifier": "id 2",
    "name": "name 2",
    "image": "path 2"
  }
]

How would I map those dictionaries into model structs?

struct Model {
    let identifier: String
    let name: String
    let image: String
}
extension Model: JSONDecodable {
    init(json value: JSON) throws {
        identifier = try value.string("identifier")
        name = try value.string("name")
        image = try value.string("image")
    }
}
jgallagher commented 8 years ago

You probably want arrayOf or map:

let json = try JSON(... your data...)
let models1 = try json.arrayOf(type: Model.self)
let models2 = try json.array().map(Model.init)
jeremy-w commented 8 years ago
class GitHubIssue138Example: XCTestCase {
    struct Model: JSONDecodable {
        let identifier: String
        let name: String
        let image: String

        init(json value: JSON) throws {
            identifier = try value.string("identifier")
            name = try value.string("name")
            image = try value.string("image")
        }
    }

    func testDecodingArrayOfModel() {
        let jsonStr = "[\n            {\n                \"identifier\": \"id 1\",\n                \"name\": \"name 1\",\n                \"image\": \"path 1\"\n        },\n        {\n            \"identifier\": \"id 2\",\n            \"name\": \"name 2\",\n            \"image\": \"path 2\"\n        }\n        ]"
        let JSON = try! Freddy.JSON(jsonString: jsonStr)
        let models: [Model]?
        switch JSON {
        case let .Array(jsonModels):
            models = try? jsonModels.map { try Model.init(json: $0) }
        default:
            models = nil
        }
        XCTAssertNotNil(models)
    }
}
trispo commented 8 years ago

Ah. Thanks. Didn't see that .array() can be called without parameters.

ghost commented 7 years ago

Hey guys,

Is this still valid syntax? Assuming data is of type Data:

let json = try JSON(data: data)
let models2 = try json.array().map(Model.init)

Keeping getting "Enum element 'array' cannot be referenced as an instance member".

jgallagher commented 7 years ago

This changed with Swift 3. Try .getArray() instead of .array().

RickDT commented 7 years ago

Could anything be done to make Xcode suggest .getArray()? When I type myJSON.arra, the suggestions I get are getArray(at: ... and decodeArray(at: ... which had me trying silly things like myJSON.getArray(at: "") until I found this thread.

Just a small improvement that would help, if it's even possible with Xcode's suggestion handling.

jgallagher commented 7 years ago

AFAIK we have no way of influencing Xcode suggestions. :(

ghost commented 7 years ago

I had a similar issue to RickDT, also solved by reading this thread. Thanks very much. 👍