Giter Site home page Giter Site logo

path-to-error's Introduction

Serde path to error

github crates.io docs.rs build status

Find out the path at which a deserialization error occurred. This crate provides a wrapper that works with any existing Serde Deserializer and exposes the chain of field names leading to the error.

[dependencies]
serde = "1.0"
serde_path_to_error = "0.1"
use serde::Deserialize;
use std::collections::BTreeMap as Map;

#[derive(Deserialize)]
struct Package {
    name: String,
    dependencies: Map<String, Dependency>,
}

#[derive(Deserialize)]
struct Dependency {
    version: String,
}

fn main() {
    let j = r#"{
        "name": "demo",
        "dependencies": {
            "serde": {
                "version": 1
            }
        }
    }"#;

    // Some Deserializer.
    let jd = &mut serde_json::Deserializer::from_str(j);

    let result: Result<Package, _> = serde_path_to_error::deserialize(jd);
    match result {
        Ok(_) => panic!("expected a type error"),
        Err(err) => {
            let path = err.path().to_string();
            assert_eq!(path, "dependencies.serde.version");
        }
    }
}

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

path-to-error's People

Contributors

dtolnay avatar kijewski avatar noib3 avatar ramosbugs avatar rreverser avatar swlynch99 avatar vorner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

path-to-error's Issues

Error doesn't implement `std::error::Error`

Hello

I'd like to ask if not implementing std::error::Error on the Error type is just an omission or if it is on purpose. If it's omission, I can submit a MR, but I'm not 100% sure what to use as the Display โ€’ maybe "{}: {}", self.path(), self.inner()?

Does not work with flatten

#[test]
fn test_flattened_struct() {
    #[derive(Deserialize, Debug)]
    struct Package {
        name: String,
        #[serde(flatten)]
        dependency: Dependency,
    }

    #[derive(Deserialize, Debug)]
    struct Dependency {
        version: String,
    }

    let j = r#"{
        "name": "demo",
        "version": 1
    }"#;

    // this doesn't work
    test::<Package>(j, "version");
    // this is the current result
    // test::<Package>(j, ".");
}

Recursive enums lose path information

This may be similar to #6 and #9.

When deserializing an enum that can recursively hold itself, like this:

enum Thing {
    Foo {
        inner: Option<Box<Thing>>,
        required_field: u32,
    },
    Bar,
}

The path returned for errors is . (a dot).

Both of these JSON strings returns the same error:

{
    "type": "Foo",
    "required_field": 1,
    "inner": {
        "type": "Foo",
        "inner": null
    }
}
{
    "type": "Foo",
    "inner": {
        "type": "Foo",
        "required_field": 2,
        "inner": null
    }
}

The first is missing required_field at the second level, and the second is missing at the top level. It is impossible to tell from the error where the problem is. The Display of the error is

.: missing field `required_field`

Full code to reproduce is here: https://gist.github.com/mythmon/e29581a739bdbbee237df11b39213d22

Expose serde_path_to_error::Error constructor

Currently, when using the serde_path_to_error::Deserializer manually, it's not possible to wrap the resulting error + track.path() into a serde_path_to_error::Error from outside the crate since its fields are hidden.

Users can create their own wrappers, but it would be more convenient to either make the fields public or expose an Error::new constructor for downstream users.

Support non-string keys in maps

This test now fails:

#[test]
fn test_hash_map_non_string_key() {
    type Map = std::collections::HashMap<i32, Dependency>;

    #[derive(Deserialize, Debug)]
    struct Dependency {
        version: String,
    }

    let j = r#"{
		"100": {
			"version": 1
		}
    }"#;

    test::<Map>(j, "100.version");
}

But it can be easily fixed like this:

fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
where
    E: de::Error,
{
    *self.key = Some(v.to_string()); // this line was added
    self.delegate.visit_i32(v)
}

I don't know which types should be added though.
I can make a pull request if you think it would be useful.

i128 and u128 are not supported

I just spent an hour debugging my deserializer to try and find an origin of the error "i128 is not supported", which, seems somewhat ironically, turned out to be in the debugging helper (path-to-error) itself.

Looking at the source, it seems that corresponding methods for i128/u128 are not propagated.

How to obtain the type of errors?

I want to use json as the request body of my webservice with a json format error reported when "bad request". But this lib, which is awesome, only reports the path of the error. How can I get the type of errors such as missed key or mismatched type?

By the way, I found that when this is a missed key, this library will report the parent.

Field name and line numbers are missing in internally tagged enum

Here is a test case that shows the failure. Comment/uncomment one of the let json_deserializer = lines to toggle between the JSON test data. The output for the failing case is:

Error parsing field: ., at position 0:0
invalid type: string "500", expected u32
#![feature(proc_macro_hygiene, decl_macro)]

use serde_derive::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(tag = "type", content = "content")]
pub enum TestStruct {
    A(u32),
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub enum Wrapper {
    B { value: TestStruct },
}

#[allow(dead_code)]
const WORKING_JSON: &'static str = r#"
{
	"type": "B",
	"value": { "type": "A", "content": 500 }
}"#;

#[allow(dead_code)]
const FAILING_FIELD_NAME_AND_LINE: &'static str = r#"
{
	"type": "B",
	"value": { "type": "A", "content": "500" }
}"#;

fn main() {
    // Example of JSON deserialization error, speficially a string is provided instead of u32.
    // Comment out one of these two lines to see the difference between the working and failing JSON
    let json_deserializer = &mut serde_json::Deserializer::from_str(WORKING_JSON);
    // let json_deserializer = &mut serde_json::Deserializer::from_str(FAILING_FIELD_NAME_AND_LINE);

    let result: Result<Wrapper, _> = serde_path_to_error::deserialize(json_deserializer);
    match result {
        Ok(e) => println!("{:?}", e),
        Err(e) => {
            let path = e.path().to_string();
            let i = e.into_inner();
            println!(
                "Error parsing field: {}, at position {}:{}\n{}",
                path,
                i.line(),
                i.column(),
                i
            )
        }
    }
}

Do not print path when there is nothing to print

I realize this may be opinionated topic, but I want to at least propose this.

When the library is passed for example an invalid JSON such as {invalid: json} The error printed is
?: key must be a string at line 1 column 2. For example in axum which uses this for more inforamtion about a deserialization error this gets turned to Failed to parse the request body as JSON: ?: key must be a string at line 1 column 2.

The ?: was a bit confusing for me and took me a while to figure out wher it came from. I would suggest to omit printing the path when it is exactly ? as it provides no information. Alternatively translate that to something else more descriptive about where the origin is coming from (this would always be an unknown top level key right?).

If this is a change you would be willing to accept I would be glad to create a PR.

Upstream to serde

Is it possible for this to be upstreamed to serde perhaps as a feature?

Support path to Serialize error

The current version of this library works for Deserialize errors only. I'd like to use it for Serialize too, which can also be fallible. For example I'd like to be able to determine the path to the following errors:

#[test]
fn test_refcell_already_borrowed() {
    #[derive(Serialize, Debug)]
    struct Outer<'a> {
        k: Inner<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Inner<'a> {
        refcell: &'a RefCell<String>,
    }

    let refcell = RefCell::new(String::new());
    let outer = Outer {
        k: Inner { refcell: &refcell },
    };

    let _borrowed = refcell.borrow_mut();

    let mut out = Vec::new();
    let ser = &mut serde_json::Serializer::new(&mut out);
    let result = serde_path_to_error::serialize(&outer, ser);
    let path = result.unwrap_err().path().to_string();
    assert_eq!(path, "k.refcell");
}
#[test]
fn test_map_nonstring_key() {
    let mut inner_map = BTreeMap::new();
    inner_map.insert(b"", 0);

    let mut outer_map = BTreeMap::new();
    outer_map.insert("k", inner_map);

    let mut out = Vec::new();
    let ser = &mut serde_json::Serializer::new(&mut out);
    let result = serde_path_to_error::serialize(&outer_map, ser);
    let path = result.unwrap_err().path().to_string();
    assert_eq!(path, "k");
}

Recursive paths are not correctly detected

use std::collections::HashMap;

use serde::*;

#[derive(Debug, Deserialize, Clone)]
pub struct Schema {
    #[serde(flatten)]
    typed: TypedSchema,
    description: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum TypedSchema {
    Object(ObjectSchema),
    Array(ArraySchema),
}

#[derive(Deserialize, Clone, Debug)]
pub struct ObjectSchema {
    properties: HashMap<String, Schema>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct ArraySchema {
    items: Box<Schema>,
}

fn main() {
    let r: Result<Schema, _> = dbg!(serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_str(json())));
    if let Err(err) = r {
        println!("path: {}", err.path());
    }
}

fn json() -> &'static str{
    r#"
{
    "type": "Object",
    "properties": {
      "subkeys": {
          "properties": {
            "emails": { "type": "Array", "items": {} },
            "subkeys": { "type": "Array", "items": {} }
        }
      }
    }
}
    "#
}
[src/main.rs:30] serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_str(json())) = Err(
    Error {
        path: Path {
            segments: [],
        },
        original: Error("missing field `type`", line: 12, column: 1),
    },
)
path: .

The errors are at emails and subkeys having empty objects, but the path was reported to be at ..

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.