I have a some JSON data that is returned from a web service. The JSON is a top-level array:
[
{
"data": "value1"
},
{
"data": "value2"
},
{
"data": "value3"
}
]
Using serde_derive to make structs I can can deserialize the data contained within the array, however, I am unable to get Serde to deserialize the top-level array.
Am I missing something, or can Serde not deserialize top level-arrays?
You can simply use a Vec:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Foo {
data: String,
}
fn main() -> Result<(), serde_json::Error> {
let data = r#"[
{
"data": "value1"
},
{
"data": "value2"
},
{
"data": "value3"
}
]"#;
let datas: Vec<Foo> = serde_json::from_str(data)?;
for data in datas.iter() {
println!("{:#?}", data);
}
Ok(())
}
If you wish, you could also use transparent:
#[derive(Serialize, Deserialize, Debug)]
#[serde(transparent)]
struct Foos {
foos: Vec<Foo>,
}
let foos: Foos = serde_json::from_str(data)?;
This allows to encapsulate your data with your type.
Related
I am using serde_json in rust, and I am calling an api and get a very large json in return.
My question is this, is this possible to de-serialize this JSON partially. By partially, I mean to some, but not all properties of the JSON response.
for Example, I have this JSON:
Object {
"age_group": String(""),
"amazon_product_url": String("https://www.amazon.com/dp/0063221489?tag=NYTBSREV-20"),
"article_chapter_link": String(""),
"asterisk": Number(0),
"author": String("Jared Kushner"),
"book_image": String("https://storage.googleapis.com/du-prd/books/images/9780063221482.jpg"),
"book_image_height": Number(500),
"book_image_width": Number(331),
"book_review_link": String(""),
"book_uri": String("nyt://book/e5ec4777-5f2f-5622-9288-9b1d96e8fe1d"),
"buy_links": Array [
Object {
"name": String("Amazon"),
"url": String("https://www.amazon.com/dp/0063221489?tag=NYTBSREV-20"),
},
Object {
"name": String("Apple Books"),
"url": String("https://goto.applebooks.apple/9780063221482?at=10lIEQ"),
},
Object {
"name": String("Barnes and Noble"),
"url": String("https://www.anrdoezrs.net/click-7990613-11819508?url=https%3A%2F%2Fwww.barnesandnoble.com%2Fw%2F%3Fean%3D9780063221482"),
}
}
Then in this case, is it possible to just catch buy_links and amazon_product_url properties and never mind others?
If you only declare the fields you need, yes it is possible to only deserialize a subset of the data:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn test(s: &str)
{
let p: Point = serde_json::from_str(s).unwrap();
println!("{:?}", p);
}
fn main()
{
test("{\"x\":0,\"y\":3}");
test("{\"x\":0,\"y\":2, \"z\": 4}");
}
Output:
Point { x: 0, y: 3 }
Point { x: 0, y: 2 }
As you can see, the "z" field that is present in the second test is ignored.
See play.rust-lang.org example.
I have json from a vendor api that looks like this:
[
"Crappy vendor unwanted string",
[
{
"key": "val",
"...": "lots more",
},
{
"it": "goes on",
}
]
]
I'm at a loss as to what to do here. Simply passing this as a vec to serde ala How to Deserialize a top level array doesn't work, as I don't know how to reference/pass the inner string and array to other structs.
I can simply do this:
let json_string: String = res.text().await?;
however, that json string is unwieldy and unformatted. Not ideal.
Taking it a step further, I've created the following, which gets me close, but doesn't seem to work:
#[derive(Deserialize)]
pub struct Outer {
pub outer_tite: String,
pub inner_vec: Vec<Inner>,
}
#[derive(Deserialize)]
pub struct Inner {
pub inner_obj: InnerObj,
}
#[derive(Deserialize)]
pub struct InnerObj {
pub key1: String,
pub key2: String,
...
}
impl Outer {
pub async fn get(...) -> Result<Vec<Outer>, ExitFailure> {
let out: Vec<Outer> = rec.json::<Vec<Outer>>().await?;
ok(out)
}
}
The result here is that the first string encountered throws an error:
value: error decoding response body: invalid type: string "Bad vendor string", expected struct Outer at line 2...
Changing the input json is not an option, as it's a vendor endpoint.
I've tried the suggestions from [How can I deserialize JSON with a top-level array using Serde?](How can I deserialize JSON with a top-level array using Serde?) - this does not work for my use case.
You can directly deserialize a struct from a list of elements. The struct fields are deserialized in order. Your example code does not work since you only have one Outer element, containing multiple InnerObj. The Inner struct you added does not match the JSON structure you have shown.
The snippet shows how you can directly deserialize into the Outer struct, which contains the String and the Vec.
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Outer {
pub outer_titel: String,
pub inner_vec: Vec<Inner>,
}
#[derive(Deserialize)]
pub struct Inner {
pub key1: String,
pub key2: String,
}
#[test]
fn deser() {
let j = r#"[
"Crappy vendor unwanted string",
[
{
"key1": "val1",
"key2": "val2"
},
{
"key1": "val1",
"key2": "val2"
}
]
]"#;
let outer: Outer = serde_json::from_str(j).unwrap();
assert_eq!("Crappy vendor unwanted string", outer.outer_titel);
assert_eq!(2, outer.inner_vec.len());
}
Playground
Hello I have a &str type variable. How can I convert it to json? for example I have &str
{"index":0,"name":"AB/CDE/FG/402/test_int4","sts":"on","time":"2021-06-05 03:28:24.044284300 UTC","value":8}
How can I convert this to json
{
"index": 0,
"name": "AB/CDE/FG/402/test_int4",
"sts": "on",
"time": "2021-06-05 03:28:24.044284300 UTC",
"value": 8
}
is there anything like python json.loads() method available or something equivalent can be done in rust?
There's a misconception here. JSON is a serialization format, its always a string. Your two blocks are both JSON and functionally identical. What json.loads() is deserialize it into a value that can be accessed and manipulated natively.
You probably want to use a serde_json::Value if you want to this JSON to be used generically.
use serde_json::Value;
fn main() {
let input = r#"{"index":0,"name":"AB/CDE/FG/402/test_int4","sts":"on","time":"2021-06-05 03:28:24.044284300 UTC","value":8}"#;
let mut object: Value = serde_json::from_str(input).unwrap();
if let Some(name) = object.get_mut("name") {
*name = "new name".into();
}
println!("{}", object);
}
{"index":0,"name":"new name","sts":"on","time":"2021-06-05 03:28:24.044284300 UTC","value":8}
Playground
Or, its typical to create a structure that reflects what the JSON is expected to be and deserialize into that type via serde:
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct Data {
index: i32,
name: String,
sts: String,
time: String,
value: i32
}
fn main() {
let input = r#"{"index":0,"name":"AB/CDE/FG/402/test_int4","sts":"on","time":"2021-06-05 03:28:24.044284300 UTC","value":8}"#;
let mut object: Data = serde_json::from_str(input).unwrap();
object.name = "new name".to_string();
println!("{:#?}", object);
}
Data {
index: 0,
name: "new name",
sts: "on",
time: "2021-06-05 03:28:24.044284300 UTC",
value: 8,
}
Playground
I don't know anything built-in, but this package can do it:
use tinyjson::{JsonParseError, JsonValue};
fn main() -> Result<(), JsonParseError> {
let s = r#"
{
"index":0,
"name":"AB/CDE/FG/402/test_int4",
"sts":"on",
"time":"2021-06-05 03:28:24.044284300 UTC",
"value":8
}
"#;
let m: JsonValue = s.parse()?;
println!("{:?}", m);
Ok(())
}
Result:
Object({
"value": Number(8.0),
"index": Number(0.0),
"name": String("AB/CDE/FG/402/test_int4"),
"time": String("2021-06-05 03:28:24.044284300 UTC"),
"sts": String("on")
})
https://docs.rs/tinyjson
I have some JSON data that looks like this which I am trying to parse in Swift.
[
[
{
a: "1",
b: "2"
},
[
{
c: "3",
},
{
d: "4",
}
]
]
]
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let myArray = json[0] as? [[AnyObject]] {
for myObject in myArray {
print("This works!\(myObject)")
}
}
However nothing I try seems to work - any help would be appreciated.
you can use SwiftyJSON - https://github.com/SwiftyJSON/SwiftyJSON
or create a class based on your JSON scheme try to parse with it.
like:
class object
{
let data = Array<subObject>()
}
class subObject
{
let subData = Array<Dictionary<AnyObject,AnyObject>>()
}
This snippet is not JSON. If it was JSON, the keys would be strings, like this:
[
[
{
"a": "1",
"b": "2"
},
[
{
"c": "3",
},
{
"d": "4",
}
]
]
]
And anyway in your screenshot we see that your JSON has already been parsed!
What you show in the image is not JSON either, but an array containing arrays and dictionaries...
But let's say your JSON is actually valid and the missing quotes are just a copy/paste problem.
Then to achieve your goal you have to cast the result of NSJSONSerialization to the correct JSON format, then you can access the inner objects.
Like this, for example:
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [[AnyObject]] {
if let myArray = json.first {
for myObject in myArray {
print("This works!\(myObject)")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
I have the example below, where 2 "unusual" things can happen:
if the status is NOK, the data element will not be included at all
some attributes of the elements inside list might be missing (in the example below, key2 is missing in the second element of list.
Is there a way to de-serialise an irregular JSON string using automatic deriving?
Which is the easiest/better way to handle this kind of irregular JSON in Rust? Can I avoid writing a very complex match based code to check for every possible combination?
extern crate serialize;
static JSON: &'static str = r#"
{
"status": {
"status": "OK"
},
"data": {
"container": {
"key": "value",
"list": [
{
"key1": "value1",
"key2": "value2"
},
{
"key1": "value1"
}
]
}
}
}"#;
#[deriving(Decodable, Show)]
struct Top {
status: Status,
data: Data,
}
#[deriving(Decodable, Show)]
struct Data {
container: Container,
}
#[deriving(Decodable, Show)]
struct Status {
status: String,
}
#[deriving(Decodable, Show)]
struct Container {
key: String,
list: Vec<KeyVals>,
}
#[deriving(Decodable, Show)]
struct KeyVals {
key1: String,
key2: String,
}
fn main() {
let result: Top = match serialize::json::decode(JSON) {
Ok(x) => x,
Err(why) => fail!("Failed decoding the JSON! Reason: {}", why),
};
println!("{}", result);
}
When running the code, it fails because the second element of the list is missing the key2 attribute.
task '<main>' failed at 'Failed decoding the JSON! Reason: MissingFieldError(key2)', hello.rs:56
Thank you
Potentially existing data can be represented via an enum. In the simplest case, with an Option.
I believe using an enum will also solve your problem.
#[deriving(Encodable, Decodable)]
enum Status {
Good(Container),
Bad,
VeryBad
}
If the Container also contains potentially existing data, then you can again use an enum to represent that.