TryFromJson

Trait TryFromJson 

Source
pub trait TryFromJson: Sized {
    // Required method
    fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
       where T: Iterator<Item = u8>;
}
Expand description

Implementing this trait makes possible for any type (except unions) to be JSON deserializable. The common technique to implement this trait is automatically through #[derive(TryFromJson)]. Every type that implements TryFromJson must also implement ValidateJson. And every deserializable field must implement Default.

§Examples

use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(Default, TryFromJson, ValidateJson, DebugToJson, PartialEq)]
struct Point {x: i32, y: i32}

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
#[json(type)]
enum Geometry
{	#[json(point)] Point(Point),
	#[json(cx, cy, r)] Circle(i32, i32, i32),
	Nothing,
}

let mut reader = Reader::new(r#" {"type": "Point", "point": {"x": 0, "y": 0}} "#.bytes());
let obj: Geometry = reader.read().unwrap();
assert_eq!(obj, Geometry::Point(Point {x: 0, y: 0}));

Here we deserialize a struct, and an enum. Struct Point {x: 0, y: 0} will be written as {"x": 0, "y": 0}.

We can use different names for “x” and “y”. Every struct field can be optionally annotated with #[json(field_name)] attribute, or #[json("field_name")].

For enums we need to give names to each field, plus to “variant” field. The name of the “variant” field is specified at enum level. In the example above, it’s “type” (#[json(type)]). So Geometry::Circle(0, 0, 1) will be written as {"type": "Circle", "cx": 0, "cy": 0, "r": 1}.

Variant name is printed as it’s called in enum (“Point”, “Circle”, “Nothing”). We can rename them if specify #[json(variant_name(field_name_0, field_name_1, ...))].

use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(Default, TryFromJson, ValidateJson, DebugToJson, PartialEq)]
struct Point {x: i32, y: i32}

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
#[json(var)]
enum Geometry
{	#[json(pnt(point))] Point(Point),
	#[json(cir(cx, cy, r))] Circle(i32, i32, i32),
	Nothing,
}

let mut reader = Reader::new(r#" {"var": "pnt", "point": {"x": 0, "y": 0}} "#.bytes());
let obj: Geometry = reader.read().unwrap();
assert_eq!(obj, Geometry::Point(Point {x: 0, y: 0}));

There’s also another option: to choose variant according to content. To do so, we ommit #[json(...)] at enum level. This is only possible if variants have non-overlapping members.

use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
struct Point {x: i32, y: i32}

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
enum Geometry
{	#[json(point)] Point(Point),
	#[json(cx, cy, r)] Circle(i32, i32, i32),
	Nothing,
}

let mut reader = Reader::new(r#" {"point": {"x": 0, "y": 0}} "#.bytes());
let obj: Geometry = reader.read().unwrap();
assert_eq!(obj, Geometry::Point(Point {x: 0, y: 0}));

To exclude a field from deserialization, and use default value for it, specify empty name (#[json("")]).

use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
struct Point {x: i32, y: i32, #[json("")] comments: String}

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
enum Geometry
{	#[json(point)] Point(Point),
	#[json(cx, cy, r)] Circle(i32, i32, i32),
	Nothing,
}

let mut reader = Reader::new(r#" {"point": {"x": 0, "y": 0, "comments": "hello"}} "#.bytes());
let obj_0: Geometry = reader.read().unwrap();
assert_eq!(obj_0, Geometry::Point(Point {x: 0, y: 0, comments: String::new()}));

let mut reader = Reader::new(r#" {"point": {"x": 0, "y": 0}} "#.bytes());
let obj_0: Geometry = reader.read().unwrap();
assert_eq!(obj_0, Geometry::Point(Point {x: 0, y: 0, comments: String::new()}));

It’s possible to validate object right after deserialization. To do so implement ValidateJson.

use nop_json::{Reader, TryFromJson, ValidateJson};

#[derive(TryFromJson, Debug)]
struct FromTo {from: i32, to: i32}

impl ValidateJson for FromTo
{	fn validate_json(self) -> Result<Self, String>
	{	if self.from <= self.to
		{	Ok(self)
		}
		else
		{	Err("to must be after from".to_string())
		}
	}
}

let mut reader = Reader::new(r#" {"from": 1, "to": 2}  {"from": 2, "to": 1} "#.bytes());
let from_to_1_2: Result<FromTo, std::io::Error> = reader.read();
let from_to_2_1: Result<FromTo, std::io::Error> = reader.read();
assert!(from_to_1_2.is_ok());
assert!(from_to_2_1.is_err());

§Ignoring fields

By default invalid object properties report errors.

use std::io;
use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
struct Point {x: i32, y: i32}

let mut reader = Reader::new(r#" {"x": 0, "y": 1, "comments": "No comments"} "#.bytes());
let obj_0: io::Result<Point> = reader.read();
assert!(obj_0.is_err());

To ignore specific fields from JSON input, use #[json_ignore(name_1, name_2)] or #[json_ignore("name_1", "name_2")]. To ignore all unknown fields: #[json_ignore].

use std::io;
use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
#[json_ignore]
struct Point {x: i32, y: i32}

let mut reader = Reader::new(r#" {"x": 0, "y": 1, "comments": "No comments"} "#.bytes());
let obj_0: Point = reader.read().unwrap();
assert_eq!(obj_0, Point {x: 0, y: 1});

In enums #[json_ignore] placed at enum level applies to all variants. Plus each variant can have it’s own ignore list.

use std::io;
use nop_json::{Reader, TryFromJson, ValidateJson, DebugToJson};

#[derive(TryFromJson, ValidateJson, DebugToJson, PartialEq)]
#[json(type)]
#[json_ignore(comments)]
enum Geometry
{	#[json(point(x, y))]
	#[json_ignore(point_comments)]
	Point(i32, i32),

	#[json(circle(cx, cy, r))]
	#[json_ignore(circle_comments)]
	Circle(i32, i32, i32),
}

let mut reader = Reader::new(r#" {"type": "point", "x": 0, "y": 1, "comments": "No comments", "point_comments": "No comments"} "#.bytes());
let obj_0: Geometry = reader.read().unwrap();
assert_eq!(obj_0, Geometry::Point(0, 1));

let mut reader = Reader::new(r#" {"type": "point", "x": 0, "y": 1, "circle_comments": "No comments"} "#.bytes());
let obj_0: io::Result<Geometry> = reader.read();
assert!(obj_0.is_err());

§Implementing TryFromJson manually

Automatic implementation through #[derive(TryFromJson)] has 1 limitation: object key string must be not longer than 128 bytes, or it will be truncated.

Sometimes there can be different reasons to implement TryFromJson manually. Let’s see what the automatic implementation does expand to.

use nop_json::{Reader, TryFromJson, ValidateJson};

struct Point {x: i32, y: i32}

impl ValidateJson for Point {}

impl TryFromJson for Point
{	fn try_from_json<T>(reader: &mut Reader<T>) -> std::io::Result<Self> where T: Iterator<Item=u8>
	{	let mut x = None;
		let mut y = None;

		reader.read_object_use_buffer
		(	|reader|
			{	match reader.get_key()
				{	b"x" => x = reader.read_prop("x")?,
					b"y" => y = reader.read_prop("y")?,
					_ => return Err(reader.format_error_fmt(format_args!("Invalid property: {}", String::from_utf8_lossy(reader.get_key()))))
				}
				Ok(())
			}
		)?;

		let result = Self
		{	x: x.unwrap_or_default(),
			y: y.unwrap_or_default(),
		};
		result.validate_json().map_err(|msg| reader.format_error(&msg))
	}
}

This implementation uses read_object_use_buffer() which reads object keys to internal buffer which is 128 bytes, without memory allocation. You can use read_object() instead to read keys longer than 128 bytes. Also you can do different things in this implementation function.

The automatic TryFromJson implementation generates JSON objects. If our struct is just a wrapper around a primitive type, we may wish to serialize it to a primitive type.

use std::{io, fmt};
use nop_json::{Reader, TryFromJson, DebugToJson, escape};

#[derive(PartialEq)]
struct Wrapper
{	value: String,
	comment: String,
}

impl TryFromJson for Wrapper
{	fn try_from_json<T>(reader: &mut Reader<T>) -> io::Result<Self> where T: Iterator<Item=u8>
	{	reader.read::<String>().map(|value| Self {value, comment: Default::default()})
	}
}
impl DebugToJson for Wrapper
{	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{	write!(f, "\"{}\"", escape(&self.value))
	}
}
impl fmt::Debug for Wrapper
{	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{	DebugToJson::fmt(self, f)
	}
}

let mut reader = Reader::new(r#" "the value" "#.bytes());
let wrp: Wrapper = reader.read().unwrap();
assert_eq!(wrp, Wrapper {value: "the value".to_string(), comment: "".to_string()});

Required Methods§

Source

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl TryFromJson for bool

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for char

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for f32

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for f64

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for i8

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for i16

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for i32

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for i64

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for i128

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for isize

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for u8

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for u16

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for u32

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for u64

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for u128

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for ()

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for usize

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl TryFromJson for String

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Option<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Box<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for BTreeMap<String, U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for BTreeSet<U>
where U: Ord + TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for LinkedList<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for VecDeque<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Rc<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Arc<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Vec<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for HashMap<String, U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for HashSet<U>
where U: Eq + Hash + TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for Mutex<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U> TryFromJson for RwLock<U>
where U: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U, V> TryFromJson for (U, V)
where U: TryFromJson, V: TryFromJson,

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Source§

impl<U, V, W> TryFromJson for (U, V, W)

Source§

fn try_from_json<T>(reader: &mut Reader<T>) -> Result<Self>
where T: Iterator<Item = u8>,

Implementors§