While extracting details from the api I'm receiving error as "keyNotFound(CodingKeys(stringValue: "propertyTypeGroup", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"propertyTypeGroup\", intValue: nil) (\"propertyTypeGroup\").", underlyingError: nil))"
What i tried here(referred some SO questions):
Given the api values as optional.
Given my array struct correctly in decoder line.
what else i need change to overcome this error?
my url-session line:
let decodedData = try JSONDecoder().decode([PreviousElement].self, from: data)
my struct:
struct PreviousElement: Codable {
let id, email: String?
let propertyTypeGroup: listingPropertyTypeGroup
let propertyType: listingPropertyType
let privacyType: String?
let location: listingLocation
let floorPlan, amenities: [PropertyTypeGroup]
let photos: [listingPhoto]
let photosPath, title: String?
let description: [PropertyTypeGroup]
let descriptionDetails, price: String?
//let legal: [String]
//let bookingInfo, bookedDates: [Any?]
let isApproved: Bool?
let firstName, lastName: String?
let isRejected, isDeactivate, isListed: Bool?
//let review, liked: [Any?]
let createdAt, updatedAt: String?
//let v: Int
}
Related
I've written a custom protocol where I've defined my own struct for a frame and it parses from bytes. My function accepts a Vec and parses the elements accordingly. To account for invalid frames, I am returning a Result<Frame> and calling .get() on the byte array. Here's my code:
fn main(){
let emptyvec = Vec::new();
match Frame::from_bytes(emptyvec) {
Err(e) => {
println!("Received invalid frame");
},
Ok(frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8, // indicates if chunked
msgtype: u8, // a flag for message type
sender: u8, // which node ID sent this frame?
routeoffset: u8, // size of array of route for frame
route: Vec<u8>, // a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> std::io::Result<Self> {
let txflag = bytes.get(0)?.clone();
let msgtype = bytes.get(1)?.clone();
let sender = bytes.get(2)?.clone();
let routesoffset = bytes.get(3)?.clone();
let routes = &bytes.get(4..(4+routesoffset as usize))?;
let (left, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data
})
}
}
However when I try to use this pattern I get the following compilation error, and when attempting to implement the trait I get an error that the Try trait is unstable.
error[E0277]: `?` couldn't convert the error to `std::io::Error`
--> src/stack/frame.rs:121:34
|
121 | let txflag = bytes.get(0)?.clone();
| ^ the trait `std::convert::From<std::option::NoneError>` is not implemented for `std::io::Error`
Not quite sure how to proceed but I'd like to use stable features to solve this. The goal here is to be able to parse bytes and handle an invalid frame as necessary.
This is probably what you want
use std::io::{Error, ErrorKind};
fn main() {
let emptyvec = Vec::new();
match Frame::from_bytes(&emptyvec) {
Err(e) => {
println!("Received invalid frame");
}
Ok(frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8,
// indicates if chunked
msgtype: u8,
// a flag for message type
sender: u8,
// which node ID sent this frame?
routeoffset: u8,
// size of array of route for frame
route: Vec<u8>,
// a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> std::io::Result<Self> {
let txflag = bytes.get(0).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let msgtype = bytes.get(1).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let sender = bytes.get(2).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let routesoffset = bytes.get(3).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let routes = bytes
.get(4..(4 + routesoffset as usize))
.ok_or(Error::from(ErrorKind::InvalidData))?
.clone();
let (_, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data,
})
}
}
Here is Rust Playground
You are trying to call ? on Option. You have to convert Option to Result (If you still want to use ?).
I want to add to what Đorðe Zeljić said:
As he already pointed out the result of bytes.get(0) is a std::option::Option. When you use the ? operator on that you already left the grounds of stable Rust. This application is only supported in unstable Rust at the moment.
If you want to stay in stable Rust, it's probably best to do what Đorðe wrote. If you want to keep using the ? operator because it produces nicer looking code, here is what's going on:
Rust has a lot of error types, each being only able to represent what they are made for. If you are using a std::io::Result this implicitly uses the error type std::io::Error which is only able to represent typical I/O errors. This type is not able to represent “there was no value when I expected one”. That's why from applying ? to a Option with the None value, you don't get a std::io::Error but a different kind of error: std::option::NoneError.
When your Rust application grows it will happen often, that you have to return a Result that can contain different types of errors. In that case you normally define your own error type (enum), that can represent different kinds of errors. Then for each error, that can be contained, you have to define the From trait on your own enum. This can be a lot of repeated work, so there is a macro in the quick-error crate, that helps with that and implements the From trait automatically for each error that can be contained.
To get your code compiling, you could define the following error enum, that can represent std::io::Error as well as std::option::NoneError:
quick_error! {
#[derive(Debug)]
pub enum FrameError {
IoError(err: std::io::Error) {from() cause(err)}
MissingValue(err: std::option::NoneError) {from()}
}
}
Instead of std::io::Result<Self> your from_bytes function then has to return a std::result::Result that uses your new error type: Result<Self, FrameError>.
Completely assembled that looks like this:
#![feature(try_trait)]
use quick_error::*;
quick_error! {
#[derive(Debug)]
pub enum FrameError {
IoError(err: std::io::Error) {from() cause(err)}
MissingValue(err: std::option::NoneError) {from()}
}
}
fn main() {
let emptyvec = Vec::new();
match Frame::from_bytes(&emptyvec) {
Err(_e) => {
println!("Received invalid frame");
}
Ok(_frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8, // indicates if chunked
msgtype: u8, // a flag for message type
sender: u8, // which node ID sent this frame?
routeoffset: u8, // size of array of route for frame
route: Vec<u8>, // a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> Result<Self, FrameError> {
let txflag = bytes.get(0)?.clone();
let msgtype = bytes.get(1)?.clone();
let sender = bytes.get(2)?.clone();
let routesoffset = bytes.get(3)?.clone();
let routes = bytes.get(4..(4 + routesoffset as usize))?;
let (left, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data,
})
}
}
To use the quick-error crate, you have to add the following to your Cargo.toml:
[dependencies]
quick-error = "1.2.3"
I created this example code:
fn main() {
let books = vec![
Book {
data: Ok("type1".to_owned()),
metadata: "meta1".to_owned(),
},
Book {
data: Err("-".to_owned()),
metadata: "meta2".to_owned(),
},
Book {
data: Ok("type2".to_owned()),
metadata: "meta2".to_owned(),
},
];
// metadata without data being error
let (book_type_1, book_type_2): &(Vec<_>, Vec<_>) = &books
.iter()
.filter(|f| f.data.is_ok())
.partition(|p| p.data.as_ref().unwrap() == "type1");
println!("Books {:?}", books);
println!("Type 1 {:?}", book_type_1); // Should be the original Vec<Book> with Type 1 filtered.
println!("Type 2 {:?}", book_type_2); // Should be the original Vec<Book> with Type 2 filtered.
}
#[derive(Debug)]
struct Book {
data: Result<String, String>,
metadata: String,
}
On the let (book_type_1, book_type_2) expression, I need to use Book::data twice, but I already filtered it so I know it can't be Err. Is there a way to restructure to remove the use of unwrap here?
I'm not sure exactly what you mean, but it seems like you want to use Iterator::filter_map(). It lets you filter for values that are Some(T), which then get passed on as unwrapped Ts.
So what you can do is convert your Results to Options with Result::ok(), so a Result::Ok(T) will become Some(T) which means it passes the filter as T.
fn main() {
let books = vec![
Book {
data: Ok("type1".to_owned()),
metadata: "meta1".to_owned(),
},
Book {
data: Err("-".to_owned()),
metadata: "meta2".to_owned(),
},
Book {
data: Ok("type2".to_owned()),
metadata: "meta2".to_owned(),
},
];
// metadata without data being error
let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
.iter()
.filter_map(|f| {
match &f.data {
Ok(data) => Some((f, data)),
Err(_) => None,
}
})
.partition(|(book, data)| *data == "type1");
println!("Books {:?}", books);
println!("Type 1 {:?}", book_type_1);
println!("Type 2 {:?}", book_type_2);
}
#[derive(Debug)]
struct Book {
data: Result<String, String>,
metadata: String,
}
playground
I removed the unnecessary reference to the returned partitioned tuple.
Also note that None pertains to Option<T>, but you're using Result<T, E>. I think you knew that but just making sure.
I would use flat_map as described in the other answer, but I'd leave the yielded values as a Result. Result implements IntoIterator, so you can use it directly in flat_map.
I'd also use Result::as_ref instead of writing out the match explicitly.
I'd then use Itertools::partition_map to simultaneously select between the types and remove the extra property:
extern crate itertools;
use itertools::{Either, Itertools};
// ...
let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
.iter()
.flat_map(|b| b.data.as_ref().map(|data| (b, data)))
.partition_map(|(b, data)| {
if data == "type1" {
Either::Left(b)
} else {
Either::Right(b)
}
});
Note:
There's no reason to take a reference to the result tuple.
This only works because you are iterating on references to books.
If you needed to operate on the owned Books, I'd move the comparison into the flat_map call and pass it along:
let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
.into_iter()
.flat_map(|book| {
book.data
.as_ref()
.ok()
.map(|data| data == "type1")
.map(|is_type1| (is_type1, book))
})
.partition_map(|(is_type1, book)| {
if is_type1 {
Either::Left(book)
} else {
Either::Right(book)
}
});
There's also the pragmatic solution of sorting all errors into the same bin as one of the types. Since you know that there won't be any errors, this has no effect:
let (book_type_1, book_type_2): (Vec<_>, Vec<_>) = books
.iter()
.filter(|f| f.data.is_ok())
.partition(|p| p.data.as_ref().ok().map_or(false, |d| d == "type1"));
See also:
What's the most idiomatic way of working with an Iterator of Results?
I am now working on a complex XML parsing.
Here is the link: https://www.reddit.com/hot/.rss
I use Alamofire to fetch data:
protocol APIUsable {
}
struct API: APIUsable {
static func fetchRedditFeedListData() -> Observable<[Entry]>{
let URL = "https://www.reddit.com/hot/.rss"
return Observable.create { observer in
Alamofire.request(URL).response { response in
guard let data = response.data else {
return
}
do {
let xml = SWXMLHash.parse(data)
let entries: [Entry] = try xml["feed"]["entry"].value()
observer.onNext(entries)
} catch let error as NSError {
print(error.localizedDescription)
}
observer.onCompleted()
}
return Disposables.create()
}
}
}
The following is the struct I build for parsing. And It works well.
struct Entry: XMLIndexerDeserializable {
let title: String
let updated: String
let category: String
let content: String
static func deserialize(_ node: XMLIndexer) throws -> Entry {
return try Entry(
title: node["title"].value(),
updated: node["updated"].value(),
category: node["category"].value(ofAttribute: "term"),
content: node["content"].value()
)
}
}
But I also want to get the image string belong to content, img, src
I have found the solution by myself.
As we can see that there is a HTML in a XML.
So, I use SWXMLHash again to parse content
let xml = SWXMLHash.parse(/*put the content String here*/)
let imageString = xml ["table"]["tr"]["td"][0]["a"]["img"].element?.attribute(by: "src")?.text
then we can get the value of src as string for future use
I'm attempting to populate a table with the contents of a JSON file downloaded from S3 (using the AWS SDK). I'm having difficulty looping through the notifications array because it doesn't seem to be an iterable object. Nil is returned when typecasting the entire object to a dictionary. I received an error stating that I cannot typecast the notifications String as an array. How can I typecast the notifications object into something I can iterate?
//JSON file
{
"notifications": [
{
"startDate": "2016-10-01 00:00:00",
"endDate": "2016-10-31 23:59:59",
"message": "October"
},
{
"startDate": "2016-11-01 00:00:00",
"endDate": "2016-11-31 23:59:59",
"message": "November"
}
]
}
//I omitted extraneous code
let task = s3.getObject(getObjectRequest)
if let output = task.result as? AWSS3GetObjectOutput{
do{
let json = try NSJSONSerialization.JSONObjectWithData((output.body! as? NSData)!, options: .AllowFragments)
//Debug code that works
print(json["notifications"]![0]) //Prints the first notification
print(json["notifications"]![0]["startDate"])
//Debug code that does not work
let opt = json["notifications"] as! NSArray //Can't typecast String as Array
//A 'for' loop does not work as well.
}catch{
print("Error serializing JSON [\(error)]")
}
}
json["notifications"] as! NSArray works but you have to first cast the result of NSJSONSerialization to the right type, in your case a dictionary:
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject] {
print(json["notifications"] as! NSArray)
}
} catch let error as NSError {
print(error.debugDescription)
}
or, if you prefer:
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary {
print(json["notifications"] as! NSArray)
}
} catch let error as NSError {
print(error.debugDescription)
}
let opt = json["notifications"] as? Array //Can't typecast String as Array
Please try this
I've read valve's JSON Serialization in Rust, Part 1 and try to run the code in the blogpost. The most complicated part is do a custom serialization for a custom struct.
I update the snippet so it can run on newest Rust nightly:
extern crate rustc_serialize;
use rustc_serialize::{json, Encodable, Encoder};
struct Person {
name: String,
age: usize,
}
impl Encodable for Person {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
match *self {
Person { name: ref p_name, age: ref p_age, } => {
s.emit_struct("Person", 0, |s| {
try!(s.emit_struct_field( "name", 0, |s| p_name.encode(s)));
try!(s.emit_struct_field( "age", 1, |s| p_age.encode(s)));
try!(s.emit_struct_field( "summary", 2, |s| {
(format!("Nice person named {}, {} years of age", p_name, p_age)).encode(s)
}));
Ok(())
})
},
}
}
}
fn main() {
let person = Person {
name: "John Doe".to_string(),
age: 33,
};
println!("{}" , json::encode(&person).unwrap());
}
The output of above is {}, but the correct result should be:
{"age":33,"name":"John Doe","summary":"Nice person named John Doe, 33 years of age"}
I want to know how to use Encodable trait to serialize a custom struct in right way.
Thank you.
It looks like your tutorial is out-of-date. It says
We call emit_struct on our encoder and pass it 3 arguments: the name of the struct, current index and an anonymous function(aka lambda). The name of the struct is not used; current index is not used too.
But the code says
fn emit_struct<F>(&mut self, _: &str, len: usize, f: F) -> EncodeResult<()> where
F: FnOnce(&mut Encoder<'a>) -> EncodeResult<()>,
{
if self.is_emitting_map_key { return Err(EncoderError::BadHashmapKey); }
if len == 0 {
try!(write!(self.writer, "{{}}"));
So the argument has changed from an index to a length, and it's now meaningful. Here's your example working:
extern crate rustc_serialize;
use rustc_serialize::{json, Encodable, Encoder};
struct Person {
name: String,
age: usize,
}
impl Encodable for Person {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("Person", 1, |s| {
try!(s.emit_struct_field("name", 0, |s| self.name.encode(s)));
try!(s.emit_struct_field("age", 1, |s| self.age.encode(s)));
try!(s.emit_struct_field("summary", 2, |s| {
let summary = format!("Nice person named {}, {} years of age", self.name, self.age);
summary.encode(s)
}));
Ok(())
})
}
}
fn main() {
let person = Person {
name: "John Doe".to_string(),
age: 33,
};
println!("{}" , json::encode(&person).unwrap());
}
Note that I also removed the crazy gyrations to destructure self and just access the properties directly.
You problem is with the emit_struct(..) call.
The prototype of this function is:
fn emit_struct<F>(&mut self, name: &str, len: usize, f: F)
-> Result<(), Self::Error>
where F: FnOnce(&mut Self) -> Result<(), Self::Error>;
Here, len is the number of fields of your struct. Bu you are setting it to 0, so the JSON dictionary generated has 0 fields.
Changing it to 3 gives this output:
{"name":"John Doe","age":33,"summary":"Nice person named John Doe, 33 years of age"}