[solved] Serde_json - deserialize unknown JSON structure to primitive types

Hello,

I have a simple need to deserialize an unknown JSON document back to rust's "raw" types, like string, integers and so on.

Here is the full code:

extern crate xmlrpc;
extern crate hyper;
extern crate argparse;
extern crate serde_json;
extern crate serde;

use serde::de::Deserializer;
use xmlrpc::{Request, RequestError, Response};
use hyper::Client;
use argparse::{ArgumentParser, Store};
use std::thread::sleep;
use std::time::Duration;

fn handle_error(err: RequestError) -> bool {
    println!("Error: {:?}", err);
    match err {
        RequestError::HyperError(_) => sleep(Duration::from_millis(1000)),
        RequestError::ParseError(_) => (),
    };

    true
}

fn handle_result(res: Response) -> bool {
    match res {
        Ok(res_fault) => println!("Result: {:?}", res_fault),
        Err(res_value) => println!("Fault: code: {}, string: {}",
            res_value.fault_code, res_value.fault_string),
    };

    false
}

struct PrgmArgs {
    url: String,
    method: String,
    json_args: String,
}

fn main() {
    let mut prgm_args = PrgmArgs {
        url: String::from(""),
        method: String::from(""),
        json_args: String::from("[]"),
    };

    {
        let mut parser = ArgumentParser::new();
        parser.set_description("Spacewalk XML-RPC API to the CLI, powered by Rust.");

        parser.refer(&mut prgm_args.url)
            .add_option(&["-U", "--url"], Store, "Full URL to XML-RPC API.")
            .required();

        parser.refer(&mut prgm_args.method)
            .add_option(&["-m", "--method"], Store, "Method to call")
            .required();

        parser.refer(&mut prgm_args.json_args)
            .add_option(&["-j", "--json-args"], Store, "Arguments as JSON Array");

        parser.parse_args_or_exit();
    }

    let xml_rpc_args: serde_json::Value = serde_json::from_str(prgm_args.json_args.as_str()).unwrap();

    let client = Client::new();
    let mut req = Request::new(prgm_args.method.as_str());

    for item in xml_rpc_args.as_array().unwrap() {
        match item {
            &serde_json::Value::Number(item_val) => req = req.arg(item_val),
        }
    }

    let mut retry: bool = true;
    while retry {
        let res = req.call(&client, prgm_args.url.as_str());

        retry = match res {
            Ok(res_ok) => handle_result(res_ok),
            Err(res_err) => handle_error(res_err),
        };
    }
}

And here is the compile error (Rust 1.15):

cargo run --release -- -U http://localhost:5001/ -m pow -j '["string", 1, [true]]'
   Compiling spaceralk v0.1.0 (file:///home/wrk/spaceralk)
error[E0277]: the trait bound `xmlrpc::Value: std::convert::From<serde_json::Number>` is not satisfied
  --> src/main.rs:72:62
   |
72 |             &serde_json::Value::Number(item_val) => req = req.arg(item_val),
   |                                                              ^^^ the trait `std::convert::From<serde_json::Number>` is not implemented for `xmlrpc::Value`
   |
   = help: the following implementations were found:
   = help:   <xmlrpc::Value as std::convert::From<i32>>
   = help:   <xmlrpc::Value as std::convert::From<bool>>
   = help:   <xmlrpc::Value as std::convert::From<std::string::String>>
   = help:   <xmlrpc::Value as std::convert::From<&'a str>>
   = help: and 3 others
   = note: required because of the requirements on the impl of `std::convert::Into<xmlrpc::Value>` for `serde_json::Number`

error: aborting due to 2 previous errors

error: Could not compile `spaceralk`.

To learn more, run the command again with --verbose.

I clearly understand what happen here, it cannot convert to xmlrpc::Value because the required trait isn't implemented. What I want is to extract the "raw" value from this serde_json::Number so I can encapsulate it into xmlrpc::Value.

I spent many hours searching a way to do so and didn't found anything, while I tried so many things :stuck_out_tongue:

Looking at docs for serde_json::Number, one can get several 64-bit wide primitive numeric types out of it. On the other hand, xmlrpc's Value has From conversions for i32 and f64. So, you can transfer the value via f64:

&serde_json::Value::Number(ref item_val) => req = req.arg(item_val.as_f64().expect("f64 value")),

Or, if you can tolerate potential truncation, via i64 cast to i32:

&serde_json::Value::Number(ref item_val) => req = req.arg(item_val.as_i64().expect("i64 value") as i32),

Ow… yes. I forgot to mention that i don't want to use a match{ } for each type. But i don't think it's possible.

But thanks for the answer and the Option.expect(), didn't know that.

Again the solution was simple…