Rust JSON Serialization - rust

I am new to Rust.
i have a JSON string in the format...
Array([
Object({
"columns": Array([
String(
"attr",
),
String(
"user_count",
),
]),
"data": Array([
Object({
"meta": Array([
Null,
Null,
]),
"row": Array([
Array([
String(
"Built-in account for administering the computer/domain",
),
]),
Number(
1,
),
]),
}),
]),
}),
])
i have created a Rust model to Serialise the JSON to...
#[derive(Serialize, Deserialize, Debug)]
pub struct ResolvedAttribute {
pub columns: Vec<Option<String>>,
pub data: Vec<MetaData>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MetaData {
pub meta: Vec<Option<String>>,
pub row: (Vec<Vec<Option<String>>>, i64),
}
when calling my API and getting the JSON - on attempting to serialise to the object (code below) I keep getting the error...
let res = REQWEST_MTLS.post(&uri).json(&req).send().await.unwrap();
//println!("res {:#?}", res);
if !res.status().is_success() {
let body: Value = res.json().await.unwrap();
println!("Error: {:#}", body);
panic!();
}
let res: Value = res.json().await.unwrap();
println!("res {:#?}", res);
let root: Vec<ResolvedAttribute> = match serde_json::from_value(res.clone()) {
Ok(r) => r,
Err(e) => {
println!("{:#}\nError:{:#?}", res, e);
panic!("Deserialization Error.");
}
};
return root
thread 'main' panicked at 'Deserialization Error.
my struct seems correct to my JSON schema - can anyone point me in the right direction?

Your row looks like this:
"row": [
["Built-in account for administering the computer/domain"],
1,
],
So you have one too many Vecs for your row field type, it should be:
pub row: (Option<Vec<Option<String>>>, i64),

Related

Convert all u64 values in JSON to string

I want to write a function in Rust that can parse a JSON string and output a modified JSON with all the number fields converted to string. All other fields should be unchanged. Structure of the JSON string is not known in advance. Here's the desired function signature:
pub fn convert_all_numbers_to_string(json: &str) -> String
Examples:
Input:
{"foo": 18446744073709551615}
Output:
{"foo": "18446744073709551615"}
Input:
{
"some": [{
"deeply": {
"nested": [{
"path": 123
}]
}
}]
}
Output:
{
"some": [{
"deeply": {
"nested": [{
"path": "123"
}]
}
}]
}
Here you go:
pub fn convert_all_ints_to_strings(json: &str) -> Result<String, serde_json::Error> {
use serde_json::Value;
fn convert_recursively(json: &mut Value) {
match json {
Value::Number(n) if n.is_u64() || n.is_i64() => {
*json = Value::String(n.to_string());
}
Value::Array(a) => a.iter_mut().for_each(convert_recursively),
Value::Object(o) => o.values_mut().for_each(convert_recursively),
_ => (),
}
}
serde_json::from_str(json).map(|mut v: Value| {
convert_recursively(&mut v);
v.to_string()
})
}
Used in a main():
fn main() {
let inp = r#"
{
"some": [{
"deeply": {
"nested": [{
"path": 123
}]
}
}]
}
"#;
let outp = convert_all_ints_to_strings(inp).unwrap();
println!("{}", outp);
}
{"some":[{"deeply":{"nested":[{"path":"123"}]}}]}

diesel: inner_join forward query

I am learning to use inner_join for forward query
pub async fn hello(pool: web::Data<DbPool>) -> HttpResult {
let conn = pool.get().map_err(ErrorInternalServerError)?;
let data: Vec<(models::Book, models::User)> = books::table
.inner_join(users::table)
.load::<(models::Book, models::User)>(&conn)
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(data))
}
I get the correct data
[
[{
"id": 1,
"name": "js",
"user_id": 1
}, {
"id": 1,
"username": "admin"
}],
[{
"id": 2,
"name": "rust",
"user_id": 1
}, {
"id": 1,
"username": "admin"
}]
]
But the structure of this json data is not what I want
I want this nested structure
[{
"id": 1,
"name": "js",
"user": {
"id": 1,
"username": "admin"
}
}, {
"id": 2,
"name": "rust",
"user": {
"id": 1,
"username": "admin"
}
}]
I don't know how to convert, is there a best practice
More information
schema:
table! {
books (id) {
id -> Unsigned<Bigint>,
name -> Varchar,
user_id -> Unsigned<Bigint>,
}
}
table! {
users (id) {
id -> Unsigned<Bigint>,
username -> Varchar,
}
}
joinable!(books -> users (user_id));
allow_tables_to_appear_in_same_query!(
books,
users,
);
models:
#[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, Debug, Clone)]
#[belongs_to(User, foreign_key = "user_id")]
#[table_name = "books"]
pub struct Book {
#[serde(skip_deserializing)]
pub id: PK,
pub name: String,
pub user_id: PK,
}
#[derive(Identifiable, Queryable, Serialize, Deserialize, Debug, Clone)]
#[table_name = "users"]
pub struct User {
pub id: PK,
pub username: String,
}
diesel version
diesel = { version = "1.4.4", features = ["mysql", "r2d2", "chrono", "numeric"] }
your real problem is with serde crate
if you have data type like this (String, i32, ...) serde will create array in output see following code
fn main() {
let val = ("Hello", 123, 2.5);
let result = serde_json::to_string(&val).unwrap();
println!("{}", result); // ["Hello",123,2.5]
}
so if you wanna solve it you can do something like this
first make your custom model
struct User {
id: i32,
username: String,
}
struct ResponseModel {
id: i32,
name: String,
user: User,
}
now implement From trait for ResponseModel
impl From<(models::Book, models::User)> for ResponseModel {
fn from(values: (models::Book, models::User)) -> Self {
Self {
id: values.0.id,
name: values.0.name,
user: User {
id: values.1.id,
username: values.1.username,
},
}
}
}
now change hello fn like this
pub async fn hello(pool: web::Data<DbPool>) -> HttpResult {
let conn = pool.get().map_err(ErrorInternalServerError)?;
let data: Vec<ResponseModel> = books::table
.inner_join(users::table)
.load::<(models::Book, models::User)>(&conn)
.map(|x| x.into_iter().map(ResponseModel::from).collect())
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(data))
}
An easy way is to use #[serde(flatten)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BookWithUser<B, U> {
#[serde(flatten)]
book: B,
user: U,
}
impl<B, U> From<(B, U)> for BookWithUser<B, U> {
fn from((b, u): (B, U)) -> Self {
Self { book: b, user: u }
}
}
#[derive(Identifiable, Associations, Queryable, Serialize, Deserialize, Debug, Clone)]
#[belongs_to(models::user::User, foreign_key = "user_id")]
#[table_name = "books"]
pub struct Book {
...
}
then use like this
#[get("/with_user")]
pub async fn list_with_user(pool: web::Data<DbPool>) -> HttpResult {
use schema::*;
let res = web::block(move || -> Result<_, DbError> {
let conn = pool.get()?;
let res = books::table
.inner_join(users::table)
.load::<(mods::book::Book, mods::user::User)>(&conn)
.map(|x| x.into_iter().map(mods::book::BookWithUser::from))?
.collect::<Vec<_>>();
Ok(res)
})
.await?
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(res))
}

How to handle multiple enums from Binary data type in single match statement in Rust

My main objective is to write the buy() function here. The function can receive either Cars or Bike enum in a Binary format.
Following is my code:
use cosmwasm_std::{from_binary, to_binary, Binary};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum Cars {
Audi { id: u32, number_plate: u32 },
Bmw { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
enum Bikes {
Yamaha { id: u32, number_plate: u32 },
Bajaj { id: u32, number_plate: u32 },
}
fn buy(prop: Binary) {
match from_binary(&prop).unwrap() {
Cars::Audi { id, number_plate } => {
println!("{:?}", (id, number_plate));
}
_ => {}
}
match from_binary(&prop).unwrap() {
Bikes::Yamaha { id, number_plate } => {
println!("{:?}", (id, number_plate));
}
_ => {}
}
}
fn main() {
let x = Cars::Audi {
id: 0,
number_plate: 1,
};
let y = Bikes::Yamaha {
id: 0,
number_plate: 1,
};
buy(to_binary(&x).unwrap());
buy(to_binary(&y).unwrap());
}
When I am trying to do it this way, its giving me this error:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseErr
{ target_type: "some_testing::Bikes", msg: "unknown variant `Audi`, expected
`Yamaha` or `Bajaj`" }', src/main.rs:23:30
What is the appropriate way of doing this?
The easiest solution would be to just merge the two traits to sidestep the issue entirely. However, I'm guessing that may not be an option for you or it would not make much conceptual sense for the types.
The way to resolve your issue is to match Ok(_) as well as the enum variable instead of unwrapping. Unwrapping will cause it to panic if it was unable to parse the data to one of your enum variants. You still will need to have two if statements though since the two values being parsed are unrelated types. Here I use if let statements instead of match statements to make it a little more concise since it only matched a single variant.
fn buy(prop: Binary) {
if let Ok(Cars::Audi { id, number_plate }) = from_binary(&prop) {
println!("{:?}", (id, number_plate));
}
if let Ok(Bikes::Yamaha { id, number_plate }) = from_binary(&prop) {
println!("{:?}", (id, number_plate));
}
}
Usually you would go with serdes Untagged enum (see documentation). You can see that it works in buy_json.
However in this library it fails with internal error: entered unreachable code. I've checked the code and found the following statement:
/// Unsupported. We rely on typed deserialization methods, even if a JSON
/// has enough type information to detect types in many cases.
///
/// See https://serde.rs/impl-deserialize.html to learn more about the differentiation
/// between deserialize_{type} and deserialize_any.
You can workaround that with something similar to buy_workaround.
use cosmwasm_std::{from_binary, to_binary, Binary, StdError};
use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_string};
#[derive(Serialize, Deserialize)]
enum Cars {
Audi { id: u32, number_plate: u32 },
Bmw { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
enum Bikes {
Yamaha { id: u32, number_plate: u32 },
Bajaj { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Wrappy {
Car(Cars),
Bike(Bikes),
}
fn buy(prop: Binary) {
match from_binary::<Wrappy>(&prop) {
Ok(ok) => match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Binary Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Binary Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
},
Err(e) => println!("{:?}", e),
}
}
fn buy_workaround(prop: Binary) {
if let Some(ok) = get_wrapper(&prop) {
match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Binary Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Binary Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
}
}
}
fn get_wrapper(prop: &Binary) -> Option<Wrappy> {
match from_binary::<Cars>(prop) {
Ok(car) => Some(Wrappy::Car(car)),
Err(StdError::ParseErr { .. }) => match from_binary::<Bikes>(prop) {
Ok(bike) => Some(Wrappy::Bike(bike)),
Err(_) => None,
},
_ => None,
}
}
fn buy_json(prop: &str) {
match from_str::<Wrappy>(prop) {
Ok(ok) => match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Json Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Json Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
},
Err(e) => println!("{:?}", e),
}
}
fn main() {
let x = Cars::Audi {
id: 0,
number_plate: 1,
};
let y = Bikes::Yamaha {
id: 0,
number_plate: 1,
};
let json = to_string(&x).unwrap();
println!("{}", json);
buy_json(&json);
buy_json(&to_string(&y).unwrap());
println!(
"Binary format: {:?}",
String::from_utf8_lossy(to_binary(&x).unwrap().as_slice())
);
buy_workaround(to_binary(&x).unwrap());
buy_workaround(to_binary(&y).unwrap());
buy(to_binary(&x).unwrap());
buy(to_binary(&y).unwrap());
}

Difficult to understand what exactly .has does with array in joi

I was trying to learn JOI to validate a schema,I came across the following content
const schema = Joi.array().items(
Joi.object({
a: Joi.string(),
b: Joi.number()
})
).has(Joi.object({ a: Joi.string().valid('a'), b: Joi.number() }))
And I validated the scheme as follow let c = arr2.validate([{ a:'a', b:'b'}]) the response I got on validation is
{
value: [ { a: 'a', b: 'b' } ],
error: [Error [ValidationError]: "[0].b" must be a number] {
_original: [ [Object] ],
details: [ [Object] ]
}
}
then I tried the above validation with the schema as follows
const arr2 = Joi.array().items(
Joi.object({
a: Joi.string(),
b: Joi.number()
})
)
even now I got the following validation response
{
value: [ { a: 'a', b: 'b' } ],
error: [Error [ValidationError]: "[0].b" must be a number] {
_original: [ [Object] ],
details: [ [Object] ]
}
}
I'm just confused as to whats the use of .has in the first schema as I can have the valid value directly as below
const arr2 = Joi.array().items(
Joi.object({
a: Joi.string().valid('a'),
b: Joi.number()
})
)
then what exact purpose is .has serving in the first schema
As stated in the .has() documentation:
Verifies that a schema validates at least one of the values in the array
The example schema...
const schema = Joi.array().items(
Joi.object({
a: Joi.string(),
b: Joi.number()
})
).has(Joi.object({ a: Joi.string().valid('a'), b: Joi.number() }))
...requires at least one of the array items to pass the validation { a: Joi.string().valid('a'), b: Joi.number() }
e.g.
{ "a": "a", "b": 12345 }
Your schema on the other hand...
const arr2 = Joi.array().items(
Joi.object({
a: Joi.string().valid('a'),
b: Joi.number()
})
)
...will only accept items that pass the validation { a: Joi.string().valid('a'), b: Joi.number() }.
The first schema will still accept something like...
[
{
"a": "foo",
"b": 12
},
{
"a": "a",
"b": 99
},
{
"a": "bar",
"b": 6
}
]
...where yours will not because not all a keys have the value 'a'.

How to create a vector of RwLock in rust?

I need to make a vector of struct. Each element of the vector have to be protected by a RwLock. Threads need to read and write in this vector ( thanks to RwLock ). How can I do that in Rust.
I've tried using a vector of Arc.
#[derive(Default, Debug, Clone, Copy)]
pub struct shared_values {
x: usize,
y: usize,
}
fn main() {
let mut shared = vec![Arc::new(RwLock::new(shared_values { x: 0, y: 0 })); 10];
//changing value of the element [0]
{
let mut sh = shared[0].write().unwrap();
*sh = shared_values { x: 10, y: 10 };
}
//Printing
{
println!("Print RwLock");
for i in 0..g.ns {
{
let val = shared[i].read().unwrap();
println!("{:?}", val);
}
}
}
}
The result is like that :
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
RwLockReadGuard { lock: RwLock { data: shared_values { suitor: 10, ws: 10 } } }
I expected the element 0 to be set with { x : 10, y : 10 }
I think Arc increase the counting reference of shared_values { x : 0 , y : 0 } but doesn't create an independant element for each index in the vector.
This vector initialization clones the parameter. Use std::iter::repeat_with:
use std::sync::{Arc, RwLock};
#[derive(Default, Debug, Clone, Copy)]
pub struct SharedValues {
x: usize,
y: usize,
}
fn main() {
let shared: Vec<_> =
std::iter::repeat_with(|| Arc::new(RwLock::new(SharedValues { x: 0, y: 0 })))
.take(10)
.collect();
//changing value of the element [0]
{
let mut sh = shared[0].write().unwrap();
*sh = SharedValues { x: 10, y: 10 };
}
//Printing
{
println!("Print RwLock");
for x in shared {
println!("{:?}", x.read().unwrap());
}
}
}
If you miss the simplicity of a macro, you can write your own:
macro_rules! vec_no_clone {
( $val:expr; $n:expr ) => {{
let result: Vec<_> = std::iter::repeat_with(|| $val).take($n).collect();
result
}};
}
fn main() {
let shared = vec_no_clone![Arc::new(RwLock::new(SharedValues { x: 0, y: 0 })); 10];
}

Resources