Panic while deserializing JSON data - json

I'm currently trying to use some API keys to access the Kucoin exchange from my program.I want to be able to have a global initialization of the keys so I can reuse them as credentials in different functions and modules.
I store the keys in a credentials.json file that looks like this:
[{
"name": "",
"api_key": "",
"api_secret": "",
"api_pass": ""
}, {
"name": "",
"api_key": "",
"api_secret": "",
"api_pass": ""
}]
And then go ahead and try to deserialize the json into a format that I can use with my functions, like so:
use kucoin_rs::{serde_json, serde_derive::{Serialize, Deserialize}};
static CREDENTIALS_JSON: &'static str = include_str!("./credentials.json");
#[derive(Serialize, Deserialize, Clone)]
pub struct ApiCredentials {
pub(crate) name: String,
pub(crate) api_key: String,
pub(crate) api_secret: String,
pub(crate) api_pass: String,
}
pub fn load_api_credentials() -> ApiCredentials {
let deserialize_creds = serde_json::from_str::<ApiCredentials(&CREDENTIALS_JSON).expect("Error deserializing JSON data");
deserialize_creds
}
The code compiles when I run cargo check, but panics when I cargo run.The error message reads
thread 'main' panicked at 'Error deserializing JSON data: Error("invalid type: map, expected a string", line: 1, column: 1)'
The message says that it expects a string, so I try to parse CREDENTIALS_JSON as string like so:
let deserialize_creds = serde_json::from_str::<ApiCredentials>(&CREDENTIALS_JSON.to_string()).expect("Unable to deserialize credentials.json!");
But I still get the same error, what I'm I doing wrong?

Your json is a list of credentials.
Deserialize it into a Vec like this:
pub fn load_api_credentials() -> Vec<ApiCredentials> {
let deserialize_creds = serde_json::from_str::<Vec<ApiCredentials>>(&CREDENTIALS_JSON).expect("Error deserializing JSON data");
deserialize_creds
}
Or return the first one:
pub fn load_api_credentials() -> ApiCredentials {
let mut deserialize_creds = serde_json::from_str::<Vec<ApiCredentials>>(&CREDENTIALS_JSON).expect("Error deserializing JSON data");
deserialize_creds.swap_remove(0)
}

Related

How can I return JSON struct with an HTTP status in Rocket 0.5.0-rc.2?

I'm searching a clean and good way to return json responses with the right HTTP status using Rocket 0.5.0-rc.2. I want something versatile that I can use to return error messages (in case of error) but also JSON results (in case of 200) from structs.
I'm using Rocket 0.5.0-rc.2 with the latest stable Rust version.
I have an Rocket API like this:
#[delete("/customers/<id>")]
pub async fn delete_customer(
db: &State<Database>,
id: String,
) -> ApiResponse<Customer> {
When I call this API with a valid id I want a status 200 with a JSON payload like this:
{
"name": "removed-customer" // this is only an example to show some data
}
When I call this API with an un-existing id I want a status 404 with a JSON payload like this:
{
"message": "unexisting id"
}
I can accept a fixed struct as json error, for instance a generic one like:
{
"message": "bad params error message",
}
with "message" and the status is the "HTTP status".
But for 200 response I need to return variable structs as JSON response, for instance to return an array of Customers if I create a get_customer API.
I saw an interesting solution HERE using a struct ApiResponse:
#[derive(Debug)]
struct ApiResponse<T> {
json: Json<T>,
status: Status,
}
impl<'r, T: serde::Serialize> Responder<'r> for ApiResponse<T> {
fn respond_to(self, req: &Request) -> response::Result<'r> {
Response::build_from(self.json.respond_to(&req).unwrap())
.status(self.status)
.header(ContentType::JSON)
.ok()
}
}
but this is not working in 0.5.0-rc.2 because Respondertakes 2 lifetime arguments, so I updated this:
#[derive(Debug)]
pub struct ApiResponse<T> {
pub json: Json<T>,
pub status: Status,
}
#[rocket::async_trait]
impl<'r, T> Responder<'r, 'r> for ApiResponse<T> {
fn respond_to(self, req: &'r Request<'_>) -> Result<'static> {
Response::build_from(self.json.respond_to(&req).unwrap())
.status(self.status)
.header(ContentType::JSON)
.ok()
}
}
But again, it isn't working, because:
the method `respond_to` exists for struct `rocket::serde::json::Json<T>`,
but its trait bounds were not satisfied E0599 method cannot be called on
`rocket::serde::json::Json<T>` due to unsatisfied trait bounds
Note: the following trait bounds were not satisfied:
`T: models::customer::_::_serde::Serialize`
which is required by `rocket::serde::json::Json<T>: rocket::response::Responder`
Probably, this is the wrong way to do this, because I have to use T = Customer in api method definition, so I cannot return a struct for Errors and Customer and the same time.
I'm open to new and better approaches.
Which is the recommended way to do this?
Is it better to use Value like this? :
#[derive(Debug)]
pub struct ApiResponse {
pub json: Value,
pub status: Status,
}
and then use it in this way?:
error case
return ApiResponse {
json: json!({"message": "error message"}),
status: Status::BadRequest,
};
or also creating a custom Error struct with "message" field?
success case
return ApiResponse {
json: serde_json::to_value(customer).unwrap(),
status: Status::BadRequest,
};

Swift JSON string to dictionary not able to parse all values

I download a JSON file from my database which returns the following string:
["ingredients": asdasdasd,
"price": 14,
"_id":
{
"$oid" = 5e8e3706f00ca80f251485c3;
},
"category": sadad,
"available": Disponibile,
"name": asdadasd]
I then convert this string to data to then convert it to a Dictionary<String, Any>
if let myData = responseString.data(using: .utf8) {
do {
let myArray = try (JSONSerialization.jsonObject(with: myData) as? [Dictionary<String, Any>])!
completion(myArray, nil)
} catch let error as NSError {
print("error:" + String(describing: error))
completion(nil, error)
}
}
This works perfectly fine, as I can get, let's say, the price parameter doing myArray["price"].
The problem arises when I try to get the Id parameter, as when I do myArray["_id"] I get:
{
"$oid" = 5e8e370af00ca80f251485cf;
}
I would like to directly get the ID parameter, and I can't parse this value to JSON as it is not in JSON format. At the moment I am fixing the issue by manipulating this string replacing the = with :, removing the ; and other nasty stuff, but I am sure there is a more efficient way to solve the issue.
myArray["_id"] is a Dictionary in your myArray.So you have to convert your myArray["_id"] to dictionary and then you can access the id.
try this
let id = (myArray["_id"] as Dictionary ?? [:])["$oid"] as? String ?? ""
What you've posted looks like debugger print output, not JSON from your server. I'm going to assume that your JSON actually looks like this:
[
{
"ingredients": "asdasdasd",
"price": 14,
"_id": {
"$oid": "5e8e3706f00ca80f251485c3"
},
"category": "sadad",
"available": "Disponibile",
"name": "asdadasd"
}
]
Given that, you could use a model struct like
struct Recipe: Codable {
let ingredients: String
let price: Int
let id: ID
let category, available, name: String
enum CodingKeys: String, CodingKey {
case ingredients, price
case id = "_id"
case category, available, name
}
}
struct ID: Codable {
let oid: String
enum CodingKeys: String, CodingKey {
case oid = "$oid"
}
}
typealias Recipes = [Recipe]
to parse it using
do {
let recipes = try JSONDecoder(Recipes.self, from: myData)
let firstOid = recipe.first?.id.oid
} catch {
print(error)
}
That said, I would recommend avoiding generic names like myArray for your variables.
Also, when retrieving JSON data from your server, it's not necessary to first convert them to a String and then back to Data before passing it to the JSON parser - simply pass the raw server data along.

How to convert Vec to JsonValue in Rust

I am querying my database and getting an Vec<Bookable> struct, using diesel library.
#[derive(QueryableByName)]
pub struct Bookable {
#[sql_type = "BigInt"]
pub id: i64,
#[sql_type = "Text"]
pub title: String
}
When I query the elements, I can access the result, but it's not possible to convert the Vec<Bookable> to json! macro:
pub fn get_terms(conn: &MysqlConnection) -> Vec<Bookable> {
diesel::sql_query(r#"SELECT title, LAST_INSERT_ID() 'id' from bookable_term;"#)
.load::<Bookable>(conn).expect("Query failed")
}
And later I call it this way:
let conn = connect();
let terms = bookable::get_terms(&conn);
json!({ "data": {
"items": terms }
})
The question is how to put the terms into this object and send the whole array to the API? I can stringify a json like this:
"items:" &vec!["to", "be", "or", "not", "to", "be"]
But when it comes to an existing Vec I get compiler error. I am using Rocket so it provides a rocket_contrib::json::JsonValue which holds json! macro
First, you derive your struct like -
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use rocket_contrib::{json::{Json}};
#[derive(QueryableByName, Deserialize, Serialize)]
pub struct Bookable {
#[sql_type = "BigInt"]
pub id: i64,
#[sql_type = "Text"]
pub title: String
}
Then you can do something like -
let conn = connect();
let terms = bookable::get_terms(&conn);
let mut data: Vec<Bookable> = HashMap::new();
data.insert("data", terms);
return Json(Vec<Bookable>);
Json(Vec<Bookable>) will also set the content-type as json for the response.

parsing JSON with a decodable?

I have a JSON file:
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""]
"aWholeLotOfnames":{
"name1": "Karl"
"name2": "pär"
}
How to parse above JSON ? I have checked this tutorial https://www.youtube.com/watch?v=YY3bTxgxWss. One text tutorial to but i don't get how to make a variable that can take a
"nameOfVar"{}
If it's not a dictionary. The tutorial are using a var nameOfVar: [what should be here in this case] for one that nearly looks like it. The thing is though that theirs are starting with a [{ and ends with a }] while mine only starts with a {? i don't know how to solve this?
Creating corresponding Swift data types for JSON is very easy.
A dictionary {} can be decoded into a class / struct where the keys become properties / members.
An array [] can be decoded into an array of the given (decodable) type.
Any value in double quotes is String even "12" or "false".
Numeric floating point values are Double, integer values are Int and true / false is Bool
null is nil
let jsonString = """
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""],
"aWholeLotOfnames":{
"name1": "Karl",
"name2": "pär"
}
}
"""
struct Item: Decodable {
let name, time, date: String
let differentTimestamps: [String]
let aWholeLotOfnames: AWholeLotOfnames
}
struct AWholeLotOfnames : Decodable {
let name1, name2 : String
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Item.self, from: data)
print(result)
} catch { print(error) }

Unit testing JSON Swift

I am working on an iOS application written in Swift which parse a lot of JSON files.
The JSON structures are sophisticated and I would to test them before to map JSON to object.
For example test if the key 'users' exists and for each user the structure('name', 'first', 'last').
{
"users": [
{
"name": {
"first": "emmi",
"last": "wiita"
}
},
{
"name": {
"first": "erdi",
"last": "nielen"
}
},
{
"name": {
"first": "daniel",
"last": "beck"
}
}
]
}
Are there any good way to do this?
Thanks.
The only way to accomplish that is really opening the JSON file and testing each property.
A good news is that since Swift 2.0 you can use guard to test if you can assign a valid value to a var or let, so you can create a function as follows:
func isValidJSON(data: NSData) -> Bool {
json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
// if "users" exists on the JSON
// AND we can cast it to an array of dictionaries
guard let users = json["users"] as [[String: AnyObject]] else {
return false
}
for user in users {
guard let name = user["name"] as [[String: AnyObject]]
firstName = name["first"] as String,
lastName = name["last"] as String else {
return false
}
}
// valid JSON
return true
}
A best practice would be also to implement the use of Exceptions instead returning false in each guard statement.
Thank you for your post #Felipe Plets. I found a good way to test JSON file.
I have implemented an enum ErrorType(Exception):
/**
Enumeration describing possible errors that occur while parsing
a message from JSON file.
*/
enum ReceiverError: ErrorType {
/**
Error trigge when the key is missed or the type.
- Parameter key: Key missed.
*/
case MissingKeyOrType(key: String)
}
then I can test all the JSON file like this:
func isValidJSON(message: [String: AnyObject]) -> throws {
guard let sosId = message["id"] as? String
else { throw ReceiverError.MissingKeyOrType(key: "sos - id") }
guard let text = message["text"] as? String
else { throw ReceiverError.MissingKeyOrType(key: "text")
}
let json = ... Get JSON
do {
try isValidJSON(json)
} catch CustomReceiver.ReceiverError.MissingKeyOrType(let key) {
print("Error: Missing key or type [key: \(key)].")
}