简体   繁体   中英

JSON parsing problem from JS REST API to Swift 4.0

I'm having a problem parsing JSON that is arriving from my JS Rest api. The casting from the JSON object to Swift 4 dictionary is not working. Here the API configuration file

{
    "name": "api",
    "version": "1.0.0",
    "description": "API",
    "main": "dist",
    "scripts": {
       "dev": "NODE_ENV=development nodemon -w src --exec babel-node src",
       "build": "babel src -s -D -d dist --preset es2015,stage-2",
    "start": "NODE_ENV=production pm2 start dist",
    "prestart": "npm run -s build",
    "lint": "eslint src"
  },
  "author": "Oscar",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-stage-2": "^6.24.1",
    "eslint": "^5.10.0",
    "nodemon": "^1.18.9"
  },
  "dependencies": {
    "body-parser": "^1.18.3",
    "dotenv": "^6.2.0",
    "express": "^4.16.4",
    "express-jwt": "^5.3.1",
    "jsonwebtoken": "^8.4.0",
    "mongoose": "^5.4.0",
    "passport": "^0.4.0",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^5.0.1"
  }
}

Here the controller file

import mongoose from 'mongoose';
import { Router } from 'express';
import FoodTruck from '../model/foodtruck';

import { authenticate } from '../middleware/authMiddleware';

export default({ config, db }) => {
    let api = Router();  

    // '/v1/foodtruck' - READ
    api.get('/', (req, res) => {
        // reading the DB looking for ALL restaurants
        FoodTruck.find({}, (err, foodTruck) => {
            if( err ) {
                res.send(err);
            }
            res.json({foodTruck});
        });
    }); 

    return api;
}

Here the model

import mongoose from 'mongoose';
import Review from './review';

let Schema = mongoose.Schema;

let FoodTruckSchema = new Schema({
   name: {
       type: String,
       required: true
   },
   foodType: {
       type: String,
       required: true
   },
   avgCost: Number,
   geometry: {
       type: { type: String, default: 'Point' },
       coordinates: {
         "lat": Number,
         "long": Number
       }
   },      
   reviews: [{
       type: Schema.Types.ObjectId, 
       ref: 'Review'   
   }]
});

module.exports = mongoose.model('FoodTruck', FoodTruckSchema);

Here the block that is going me crazy! (Please check the line after the comment // THE NEXT LINE)

static func parseFoodTruckJSONData(data: Data) -> [FoodTruck] {
        var foodTrucks = [FoodTruck]()

        do {
            let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)

            //Parse JSON data
            // THE NEXT LINE *****************************
            if let trucks = jsonResult as? [Dictionary<String, AnyObject>] { 
                for truck in trucks {
                    let newTruck = FoodTruck()
                    newTruck.id = truck["_id"] as! String
                    newTruck.name = truck["name"] as! String
                    newTruck.foodType = truck["foodType"] as! String
                    newTruck.avgCost = truck["avgCost"] as! Double

                    let geometry = truck["geometry"] as! Dictionary<String, AnyObject>
                    newTruck.geomType = geometry["type"] as! String
                    let coords  = geometry["coordinates"] as! Dictionary<String, AnyObject>
                    newTruck.lat = coords["lat"] as! Double
                    newTruck.long = coords["long"] as! Double

                    newTruck.title = newTruck.name
                    newTruck.subtitle = newTruck.foodType

                    foodTrucks.append(newTruck)

                    print("(parseFoodTruckJSONData) Added a new food truck \(newTruck.id)")
                }
            } else {
                print("(parseFoodTruckJSONData) Something is going wrong with food trucks parsing...")
            }

        }catch let err {
            print(err)
        }

    return foodTrucks
}

And I'm calling this function here

// Get all foodtrucks
func getAllFoodTrucks() {
        let sessionConfig = URLSessionConfiguration.default

        // Create session, and set a URLSessionDelegate
        let session = URLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)

        // Create the request
        // Get all foodtrucks
        guard let URL = URL(string: GET_ALL_FOODTRUCKS_URL) else { return }
        var request = URLRequest(url: URL)
        request.httpMethod = "GET"

        let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error? ) -> Void in
            if(error == nil){
                // Success
                let statusCode = (response as! HTTPURLResponse).statusCode
                print("(getAllFoodTrucks) URL session task succeeded: HTTP \(statusCode)")
                if let data = data {
                    // THE NEXT LINE *****************************
                    self.foodTrucks = FoodTruck.parseFoodTruckJSONData(data: data)
                    if(self.foodTrucks.count>0) {
                        self.delegate?.trucksLoaded()
                    }
                }
            }else{
                // Failure
                print("(getAllFoodTrucks) URL session task failed: \(error!.localizedDescription)")
            }
        })
        task.resume()
        session.finishTasksAndInvalidate()
}

The call parsing is returning an empty list. Naturally my database is not empty.

Here the JSON

{
    "foodTruck": [
        {
            "geometry": {
                "coordinates": [
                    35.89,
                    -89.324
                ],
                "type": "Point"
            },
            "reviews": [
                "5c20a26b92e52a2a1fc4435b",
                "5c20a2dd92e52a2a1fc4435c"
            ],
            "_id": "5c209fa09eec9429efc2a893",
            "name": "Joe's Stabbin Wagon",
            "foodType": "Meat",
            "avgCost": 5.99,
            "__v": 2
        },
        {
            "geometry": {
                "coordinates": [
                    38.89,
                    -86.324
                ],
                "type": "Point"
            },
            "reviews": [],
            "_id": "5c20ce386151a32af5423f1e",
            "name": "Smoki truck",
            "foodType": "Hamburger",
            "avgCost": 14.99,
            "__v": 0
        }
    ]
}

Is there something that is not arriving correctly from the server?

Thank you very much!

UPDATE:

I found the mistake. The JSON start with a "foodTruck" entity so this is a Dictionary. The correct cast of it is:

if let jsonTrucks = jsonResult as? [String: Any] {
   let trucks = jsonTrucks["foodTruck"] as! [[String: Any]]
   for truck in trucks {
      //...
   }
} else {
   print("(parseFoodTruckJSONData) Something is going wrong with food trucks parsing...")
}

Also you can use feature of Swift - Codable and work with objects like here:

class FoodTruckAPIModel: Codable {
    let foodTruck: [FoodTruck]?

    init(foodTruck: [FoodTruck]?) {
        self.foodTruck = foodTruck
    }
}

class FoodTruck: Codable {
    let geometry: Geometry?
    let reviews: [String]?
    let id, name, foodType: String?
    let avgCost: Double?
    let v: Int?

    enum CodingKeys: String, CodingKey {
        case geometry, reviews
        case id = "_id"
        case name, foodType, avgCost
        case v = "__v"
    }

    init(geometry: Geometry?, reviews: [String]?, id: String?, name: String?, foodType: String?, avgCost: Double?, v: Int?) {
        self.geometry = geometry
        self.reviews = reviews
        self.id = id
        self.name = name
        self.foodType = foodType
        self.avgCost = avgCost
        self.v = v
    }
}

class Geometry: Codable {
    let coordinates: [Double]?
    let type: String?

    init(coordinates: [Double]?, type: String?) {
        self.coordinates = coordinates
        self.type = type
    }
}

and create internal apiModel like here:

let foodTruckAPIModel = try? JSONDecoder().decode(FoodTruckAPIModel.self, from: jsonData)

and that's all:)

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