I need to get the name from a base64 value, I tried like following but I didn't able to parse it and get the name property, any idea how can I do it ?
extern crate base64;
use serde_json::Value;
fn main() {
let v = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
let bytes = base64::decode(v).unwrap();
println!("{:?}", bytes);
let v: Value = serde_json::from_slice(bytes);
}
The value represnt json like
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1df0f644a139f8d526a44af8abf78e8e
At the end I need to print "name": "John Doe"
this is the decoded value
Masklinn explains why you have an error, and you should read his answer.
But IMO the simplest and safest solution is to use serde's derive to decode into the desired structure:
use serde::Deserialize;
/// a struct into which to decode the thing
#[derive(Deserialize)]
struct Thing {
name: String,
// add the other fields if you need them
}
fn main() {
let v = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
let bytes = base64::decode(v).unwrap(); // you should handle errors
let thing: Thing = serde_json::from_slice(&bytes).unwrap();
let name = thing.name;
dbg!(name);
}
Denys has provided for the usual way to use serde, and you should definitely apply their solution if that's an option (if the document is not dynamic).
However:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1df0f644a139f8d526a44af8abf78e8e
Have you considered at least following the indications of the compiler? The Rust compiler is both pretty expressive and very strict, if you give up at every compilation error without even trying to understand what's happening, you'll have a very hard time.
If you try to compile your snippet, the first thing it tells you is
error[E0308]: mismatched types
--> src/main.rs:12:43
|
12 | let v: Value = serde_json::from_slice(bytes);
| ^^^^^
| |
| expected `&[u8]`, found struct `Vec`
| help: consider borrowing here: `&bytes`
|
= note: expected reference `&[u8]`
found struct `Vec<u8>`
and while it doesn't always work out perfectly, the compiler's suggestion is spot-on: base64 has to allocate space for the return value so it yields a Vec, but serde_json doesn't really care where the data comes from so it takes a slice. Just referencing (applying the & operator) the vec allows rustc to coerce it to a slice.
The second suggestion is:
error[E0308]: mismatched types
--> src/main.rs:12:20
|
12 | let v: Value = serde_json::from_slice(bytes);
| ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Value`, found enum `Result`
| |
| expected due to this
|
= note: expected enum `Value`
found enum `Result<_, serde_json::Error>`
which doesn't provide a solution but a simple unwrap would do for testing. However you really want to read up on Rust error handling as Result is very much the normal way to signal fallibility / errors, and thus like compiler errors you will also encounter it a lot.
Anyway that yields a proper serde_json::value::Value, which you can manipulate the normal way e.g.
v.get("name").and_then(Value::as_str));
will return an Option<&str> of value None if the key is missing or not mapping to a string, and Some(s) if the key is present and mapping to a string: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5025d399644694b4b651c4ff1b9125a1
Related
This question already has answers here:
How to fix lifetime error when function returns a serde Deserialize type?
(2 answers)
Closed 5 months ago.
I am trying to write a generic code that reads a json file into an object. But it seems that there is something I am missing here.
use serde::Deserialize;
use std::{error::Error, fs::File, io::Read};
pub fn from_file<'a, T>(filename: String) -> Result<T, Box<dyn Error>>
where
T: Deserialize<'a>,
{
let mut file = File::open(filename)?;
let mut content: String = String::new();
file.read_to_string(&mut content)?;
Ok(serde_json::from_str::<T>(&content)?)
}
I am getting the following error
error[E0597]: `content` does not live long enough
--> src\util\file.rs:11:34
|
4 | pub fn from_file<'a, T>(filename: String) -> Result<T, Box<dyn Error>>
| -- lifetime `'a` defined here
...
11 | Ok(serde_json::from_str::<T>(&content)?)
| --------------------------^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `content` is borrowed for `'a`
12 | }
| - `content` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `flavour` due to previous error
What I understand is that I have to bound T, thanks for this SO question. But I am uncertain which bounds to add.
And how to infer the required bounds for such problems. I tried to read from_str and found that it requires T: de::Deserialize<'a> only.
The lifetime argument to Deserialize<'de> indicates the lifetime of the data you're deserializing from. For efficiency reasons, serde is allowed to borrow data straight from the structure you're reading from, so for example if your JSON file contained a string and the corresponding Rust structure contained a &str, then serde would borrow the string directly from the JSON body. That means that the JSON body, at least for Deserialize, has to live at least as long as the structure deserialized from it, and that length of time is captured by the 'de lifetime variable (called 'a in your example).
If you want to read data without borrowing anything from it, you're looking for DeserializeOwned. From the docs,
Trait serde::de::DeserializeOwned
A data structure that can be deserialized without borrowing any data from the deserializer.
This is primarily useful for trait bounds on functions. For example a from_str function may be able to deserialize a data structure that borrows from the input string, but a from_reader function may only deserialize owned data.
So if your bound is DeserializeOwned (which is really just for<'de> Deserialize<'de>), then you can read data from the string without borrowing anything from it. In fact, since you're reading from a file, you can use from_reader, which does so directly, and you don't even have to worry about the intermediate string at all.
Hopefully this is enough context for the question...
Using Handlebars with Rust, I'm trying to implement a handler to handle this input:
{{toJSON JSON_OBJ_OR_NONE}}
where JSON_OBJ_OR_NONE is either a valid fragment of JSON like
{
"abc": 123,
"def": ["g", "h", "i"],
"jkl": {
"m": 1,
"n": "op"
}
}
or nothing (an empty string).
What it should return is either a pretty-printed version of the supplied JSON, or "{}" if JSON_OBJ_OR_NONE is empty.
The supplied JSON fragment is completely arbitrary; it could contain any valid JSON, or an empty string. Output should be pretty-printed.
I've tried to implement this in a bunch of different ways, and I'm currently at
handlebars_helper!(toJSON: |json_obj_or_none: str|
if json_obj_or_none.is_empty() {
"{}"
} else {
let s = serde_json::to_string_pretty(&json_obj_or_none).is_ok().to_string();
&s[..]
});
This looks close, but I'm seeing
error[E0597]: `s` does not live long enough
--> src/main.rs:145:10
|
145 | &s[..]
| -^----
| ||
| |borrowed value does not live long enough
| borrow later used here
146 | });
| - `s` dropped here while still borrowed
when I compile it
While this seems to be close to a working solution, I suspect there's a much more elegant way of coding it.
Code like:
{
let s = something;
&s
}
is almost always a mistake in Rust. References always borrow from something, and references can't exist after that thing is destroyed. Local variables are destroyed at the end of their scope.
So what this means is:
Make s
Borrow from s
Destroy s and its content
Return a reference to that thing, which has just been destroyed
So you need to pass ownership:
{
let s = something;
s // not borrowed!
}
or ensure that s isn't destroyed to soon by moving let to a scope level up:
let s;
{
s = something;
&s
// `s` isn't destroyed here, because `let s` was outside `{}`
}
// `s` and `&s` are destroyed now
If you mix it with string literals in if/else, Rust will complain that String and &str are different. Use String::from("") for the literal, or see Cow type that holds both. Rust needs to know which strings to free (String) and which it must never free (&str), that's why there are two types.
Just to wrap this up, I eventually got it working as follows:
handlebars_helper!(toJSON: |json_obj_or_none: object|
if json_obj_or_none.is_empty() {
"{}".into()
} else {
let s = serde_json::to_string_pretty(&json_obj_or_none).unwrap_or_else(|_| "{}".to_string());
s
});
This question already has answers here:
"borrowed value does not live long enough" seems to blame the wrong thing
(2 answers)
Cannot move out of value which is behind a shared reference when unwrapping
(2 answers)
Closed 3 years ago.
I am trying to parse JSON line-by-line from stdin using the pom library.
I've stolen the json implementation provided on the homepage (and have omitted that code below; it's not relevant), and am getting a borrow error from the following code:
fn main() {
for line in io::stdin().lock().lines() {
let line2 = line.unwrap().as_bytes();
let _value = json().parse(line2).unwrap();
}
}
The error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:73:23
|
73 | let tmpline = line.unwrap().as_bytes();
| ^^^^^^^^^^^^^------------ temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
| argument requires that borrow lasts for `'static`
.parse in the pom libray has the type:
pub fn parse(&self, input: &'a [I]) -> Result<O>
.as_bytes() has the type:
pub fn as_bytes(&self) -> &[u8]
Obviously, I'm borrowing incorrectly here, but I'm not entirely sure how to fix this.
The problem here is that you're using a reference to a value whose lifetime is shorter than you need, and lies in this line: line.unwrap().as_bytes().
as_bytes() returns a reference to the underlying slice of u8s. Now, that underlying slice, returned by unwrap(), happens to be a temporary which will die at the end of the statement.
In Rust, you can re-declare variables with the same name in the current scope and they will shadow the one(s) previously defined. To fix the problem, store the value somewhere, and then get a reference to it. Like so:
fn main() {
for line in io::stdin().lock().lines() {
let line = line.unwrap();
let bytes = line.as_bytes();
let _value = json().parse(bytes).unwrap();
}
}
Now the value returned by as_bytes() can point to something that lives as long as the current scope. Previously, instead, you had this:
fn main() {
for line in io::stdin().lock().lines() {
let line2 = line.unwrap().as_bytes(); // <-- the value returned by unwrap dies here
let _value = json().parse(line2).unwrap(); // <-- line2 would be dangling here
}
}
line.unwrap() returns a String, which you then borrow from with as_bytes(). Since you never bind the String itself, only the borrowed byte slice, the String is dropped at the end of the statement, and the borrowed byte slice is invalidated.
Bind the temporary String to a variable with let s = line.unwrap(), then pass s.as_bytes() to json().parse.
I'd like to read multiple JSON objects from a file/reader in Rust, one at a time. Unfortunately serde_json::from_reader(...) just reads until end-of-file; there doesn't seem to be any way to use it to read a single object or to lazily iterate over the objects.
Is there any way to do this? Using serde_json would be ideal, but if there's a different library I'd be willing use that instead.
At the moment I'm putting each object on a separate line and parsing them individually, but I would really prefer not to need to do this.
Example Use
main.rs
use serde_json;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let stdin = std::io::stdin();
let stdin = stdin.lock();
for item in serde_json::iter_from_reader(stdin) {
println!("Got {:?}", item);
}
Ok(())
}
in.txt
{"foo": ["bar", "baz"]} 1 2 [] 4 5 6
example session
Got Object({"foo": Array([String("bar"), String("baz")])})
Got Number(1)
Got Number(2)
Got Array([])
Got Number(4)
Got Number(5)
Got Number(6)
This was a pain when I wanted to do it in Python, but fortunately in Rust this is a directly-supported feature of the de-facto-standard serde_json crate! It isn't exposed as a single convenience function, but we just need to create a serde_json::Deserializer reading from our file/reader, then use its .into_iter() method to get a StreamDeserializer iterator yielding Results containing serde_json::Value JSON values.
use serde_json; // 1.0.39
fn main() -> Result<(), Box<dyn std::error::Error>> {
let stdin = std::io::stdin();
let stdin = stdin.lock();
let deserializer = serde_json::Deserializer::from_reader(stdin);
let iterator = deserializer.into_iter::<serde_json::Value>();
for item in iterator {
println!("Got {:?}", item?);
}
Ok(())
}
One thing to be aware of: if a syntax error is encountered, the iterator will start to produce an infinite sequence of error results and never move on. You need to make sure you handle the errors inside of the loop, or the loop will never end. In the snippet above, we do this by using the ? question mark operator to break the loop and return the first serde_json::Result::Err from our function.
I am relying on rustc_serialize to parse JSON data into a struct, Orders, which represents a Vec of Order structs. The JSON data may have an array or a null value; my intent is to either parse the array of orders normally, if any, or parse the null value as an Orders with an empty Vec. If neither of these is the case, then an error is to be relayed. This is my attempt:
impl Decodable for Orders {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
let try = d.read_seq(|d, l| {
let mut orders = Vec::new();
for _ in 0..l {
let order = try!(Decodable::decode(d));
orders.push(order);
}
Ok(Orders(orders))
});
match try {
value # Ok(_) => value,
error # Err(e) => match e {
ExpectedError(_, x) if &x == "null" => Ok(Orders(Vec::new())),
_ => error,
},
}
}
}
My issue has to do with pattern matching on ExpectedError. The compiler gives me the following error:
expected `<D as rustc_serialize::serialize::Decoder>::Error`,
found `rustc_serialize::json::DecoderError`
(expected associated type,
found enum `rustc_serialize::json::DecoderError`) [E0308]
src/api/types/json.rs:125 ExpectedError(_, x) if &x == "null" => Ok(Orders(Vec::new())),
^~~~~~~~~~~~~~~~~~~
I am stumped on this one. How can I correct this?
How can I correct this?
In general, you would have to choose between being generic or specialized. You cannot pattern match on an associated type because this type can be anything and a generic method should work for any type which satisfies the constraints.
For example, in your case:
<D as rustc_serialize::serialize::Decoder>::Error can be anything
rustc_serialize::json::DecoderError is but one possibility
So you should normally choose between using some abstract D or specializing the decoding for json.
However, since you are implementing Decodable here, you cannot choose NOT to be generic as you do not get to pick the signature.
Furthermore, it appears that rustc_serialize does not support tentative parsing (or at least, its json implementation does not), so I am afraid that you cannot check for nullity (using d.read_nil()) beforehand.
I suppose those limitations can be seen as the reason that this library is being retired in favor of serde, and can only encourage you to try it out.