Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Serde to deserialize malformed JSON with True/False?

Tags:

How can I deserialize the following malformed JSON using Rust's serde:

{
  "value": True
}

Using this answer, I tried the following solution:

#[macro_use]
extern crate serde_derive; // 1.0.66
extern crate serde; // 1.0.66
extern crate serde_json; // 1.0.21


use serde::de;
use std::fmt;

#[derive(Debug, PartialEq, Deserialize)]
pub struct Foo {
    #[serde(deserialize_with = "deserialize_capitalized_bool")]
    pub bar: bool,
}

fn deserialize_capitalized_bool<'de, D>(
    deserializer: D,
) -> Result<bool, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct CapitalizedBoolVisitor;

    impl<'de> de::Visitor<'de> for CapitalizedBoolVisitor {
        type Value = bool;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a True or False string")
        }

        fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            if v == &['T' as u8, 'r' as u8, 'e' as u8] {
                Ok(true)
            } else if v
                == &['F' as u8, 'a' as u8, 'l' as u8, 's' as u8, 'e' as u8]
            {
                Ok(false)
            } else {
                unimplemented!();
            }
        }
    }

    deserializer.deserialize_any(CapitalizedBoolVisitor)
}

fn main() {
    let json = r#"{
        "bar": True
    }"#;

    let foo: Foo = serde_json::from_str(json).unwrap();

    let expected = Foo {
        bar: true
    };
    assert_eq!(foo, expected);
}

runnable on the playground

The problem, as far as I can tell, is that the input is not recognized as any correct type, so none of the visitor APIs work here.

UPDATE (2020-02-05):

Apparently this is not possible to solve with serde_json (one way would be to use a custom data format or to fork serde_json to add this functionality, as serde_json does not deal with invalid input, see the maintainer's answer).

A hacky solution, for anyone else having similar issues, is to replace the True and False instances in the raw response string to true and false. This is definitely not perfect, since if a string contains True or False, those would be replaced also, but it is perhaps an acceptable solution for specific use cases.

like image 979
mandreyel Avatar asked Feb 03 '20 22:02

mandreyel


People also ask

What is JSON SerDe?

The Hive JSON SerDe is commonly used to process JSON data like events. These events are represented as single-line strings of JSON-encoded text separated by a new line. The Hive JSON SerDe does not allow duplicate keys in map or struct key names.

How does rust SerDe work?

Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. The Serde ecosystem consists of data structures that know how to serialize and deserialize themselves along with data formats that know how to serialize and deserialize other things.


1 Answers

In general you can only deserialize input with a particular library if the input is in the data format that the library is for.

So for examples, if your input is not JSON, CBOR, MessagePack then you can't use serde_json, serde_cbor, or serde_messagepack to deserialize it.

The input you show appears to be YAML so you could try serde_yaml.

fn main() {
    let input = r#" {
                      "value": True
                    } "#;
    println!("{:#?}", serde_yaml::from_str::<serde_yaml::Value>(input).unwrap());
}

In other words the fact that data is invalid JSON tells you what library not to use — serde_json. To find the right library it's more useful to find a format that the data is valid in.

like image 150
dtolnay Avatar answered Sep 21 '22 22:09

dtolnay