Modeling complex JSON

I wrote a little app after watching your tutorial videos. It models a JSON array of dictionaries:

[
{
“__type”: “CollectionSet:#MobileServices”,
“id”: 1,
“operationSuccess”: true,
“SetName”: “Spring 2 2018”
},
{
“__type”: “CollectionSet:#MobileServices”,
“id”: 2,
“operationSuccess”: true,
“SetName”: “Summer 1 2018”
},
{
“__type”: “CollectionSet:#MobileServices”,
“id”: 3,
“operationSuccess”: true,
“SetName”: “Fall 1 2018”
}
]

The model is:

final class CollectionSet: Codable {
var id: Int?
var __type: String
var operationSuccess: Bool
var SetName: String

init(__type: String, operationSuccess: Bool, SetName: String) {
    self.__type = __type
    self.operationSuccess = operationSuccess
    self.SetName = SetName
}

}

extension CollectionSet: SQLiteModel {}
extension CollectionSet: Content {}
extension CollectionSet: Migration {}

And the controller is:

struct CollectionSetsController: RouteCollection {
func boot(router: Router) throws {
let collectionsRoute = router.grouped(“api”, “getcollections”)
collectionsRoute.get(use: getAllHandler)
collectionsRoute.post(use: createHandler)
}

func getAllHandler(_ req: Request) throws -> Future<[CollectionSet]> {
    return CollectionSet.query(on: req).all()
}

func createHandler(_ req: Request) throws -> Future<CollectionSet> {
    let  collectionsSet = try req.content.decode(CollectionSet.self)
    return collectionsSet.save(on: req)
}

}

extension CollectionSet: Parameter {}

THIS CODE WORKS AS EXPECTED

I want to model the following JSON:

{
“getCollectionsResult”: [
{
“__type”: “CollectionSet:#MobileServices”,
“operationSuccess”: true,
“SetName”: “Spring 2 2018”
},
{
“__type”: “CollectionSet:#MobileServices”,
“operationSuccess”: true,
“SetName”: “Summer 1 2018”
}
]
}

What would the model and handlers look like to do this? I’ve tried several things that did not work that I won’t take up space with here. Thank you.

@rcutshaw so to do this you are going to need to create some custom Content structs to return, instead of a model (similar to how it is done in the Leaf videos). For instance, your Content type would look like:

struct CollectionResultsResponse: Content {
  let getCollectionsResult: [CollectionResult]
}

struct CollectionResult: Content {
  let __type: String
  let operationSuccess: Bool
  let SetName: String
}

You can then construct those types are required and return them

Thank you for pointing me in the right direction. I’ll go back through the Leaf videos.

1 Like

Do you have any examples of how to use these custom content structs in a controller? I’ve gone back through the leaf videos, but I don’t see where this is done. Thanks to anyone who can provide code for a getAllCollectionsHandler.

@0xtim Can you please help with this when you get a chance? Thank you - much appreciated! :]

Hi sorry, missed this notification! So to give you an example, lets say we have a route that I want to return all of the acronyms and all users in JSON. To do so I’d define a new type:

struct AcronymsAndUsers: Content {
  let acronyms: [Acronym]
  let users: [User]
}

To use it in my handler, I’d do something like:

getAcronymsAndUsersHandler(req: Request) throws -> Future<AcronymsAndUsers> {
  return try Acronym.query(on: req).all().flatMap { acronyms in
    return try User.query(on: req).all().map { users in
      return AcronymsAndUsers(acronyms: acronyms, users: users)
    }
  }
}

Does that help?

I’m still confused. I tried using this:

struct CollectionResultsResponse: Content {
var id: Int?
let getCollectionsResult: [CollectionSet]
}

final class CollectionSet: Content {
var id: Int?
var __type: String
var operationSuccess: Bool
var SetName: String

init(__type: String, operationSuccess: Bool, SetName: String) {
    self.__type = __type
    self.operationSuccess = operationSuccess
    self.SetName = SetName
}

}

extension CollectionResultsResponse: MySQLModel {}
extension CollectionResultsResponse: Migration {}

extension CollectionSet: MySQLModel {}
extension CollectionSet: Migration {}

func getResponseHandler(req: Request) throws → Future {
return try CollectionSet.query(on: req).all().flatMap { collectionSets in
return CollectionResultsResponse(from: collectionSets)
}
}

I got an error "Argument type ‘[CollectionSet]’ does not conform to expected type 'Decoder’ on the return CollectionResultsResponse line.” It suggests “as! Decoder”.

If I make this change, I get "“Cannot convert value of type '() → CollectionResultsResponse’ to
expected argument type '([CollectionSet]) → EventLoopFuture<
>’ for the return try line.

I thought that adhering to Content gave us Codable (Encodable and Decodable) as well.

The line "func getResponseHandler lost its "Future< CollectionResultsResponse> in the text in the message above, but it’s there in the code.

This is the json I want to work with:

let collectionSetsJson = “”"
{
“getCollectionsResult”: [
{
“__type”: “CollectionSet:#MobileServices”,
“operationSuccess”: true,
“SetName”: “Spring 2 2018”
},
{
“__type”: “CollectionSet:#MobileServices”,
“operationSuccess”: true,
“SetName”: “Summer 1 2018”
}
]
}
“”".data(using: .utf8)!

Thanks!

@0xtim Do you have any feedback about this? Thank you - much appreciated! :]

I’ve not been able to create code that will work to return the json above. Any help will be greatly appreciated.

@rcutshaw

Don’t know if you have solved this yet, so here goes!

Is this the model for your data?

final class CollectionSet: Codable {
  var id: Int?
  var __type: String
  var operationSuccess: Bool
  var SetName: String

Take a look at the structs that @0xtim listed earlier.

struct CollectionResultsResponse: Content {
  let getCollectionsResult: [CollectionResult]
}

struct CollectionResult: Content {
  let __type: String
  let operationSuccess: Bool
  let SetName: String
} 

There may be more ways to do this, but I was able to get this to work.
The following handler should produce your desired JSON.

func getResultsHandler(_ req: Request) throws -> Future<CollectionResultsResponse> {
  return CollectionSet.query(on: req).decode(data: CollectionResult.self).all().map { results in
    return CollectionResultsResponse(getCollectionsResult: results)
  }
}

Hope that solves it for you. Have fun!

1 Like

Since you have get array list so i recommend you using .package(url: “GitHub - vapor-community/pagination: Simple Vapor 3 Pagination”, from: “1.0.0”),

Then you will have
image

or if you want only show your type, check the example below
image
then you get what you want
image
Sorry my english is not good to explain!

1 Like