Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option<()> value doesn't round trip through JSON #1690

Closed
mwu-tow opened this issue Dec 9, 2019 · 3 comments
Closed

Option<()> value doesn't round trip through JSON #1690

mwu-tow opened this issue Dec 9, 2019 · 3 comments

Comments

@mwu-tow
Copy link

mwu-tow commented Dec 9, 2019

Please consider the following:

fn main() {
    let value = Some(());
    let str = serde_json::to_string(&value).unwrap();
    let value2 = serde_json::from_str(&str).unwrap();
    assert_eq!(value, value2);
}

I have a value of Option<()> type. However, serde_json serializes both its values (i.e. Some(()) and None) as null — that gets deserialized as None.
The example shows that we deserialize None despite serializing Some. I'd expect to read the value that I stored.

@dtolnay
Copy link
Member

dtolnay commented Dec 9, 2019

Be aware that neither serde nor serde_json makes round trip guarantees in general.

What JSON would you expect to get for each of the two values?

@mwu-tow
Copy link
Author

mwu-tow commented Dec 9, 2019

Be aware that neither serde nor serde_json makes round trip guarantees in general.

Good to know, though now you've got me worried. Is there any documentation I can follow? I wonder what are other known cases where round trip fails for built-in serialization formats?
(I wouldn't be surprised for some floating point tricky values etc. — but apart from that?)

What JSON would you expect to get for each of the two values?

Now things fail for any optional value that serializies to null. Either Option shouldn't be flattened but have separate nodes to tell apart Some and None; or unit value shouldn't use null but… anything else, like an empty object. For example, for Some(()): {} and for None: null

But then — this likely should be consistent for all unit-like types, like struct MyUnit;.

For now I guess I can try using something like struct MyUnit{} instead of ().

@niedzielski
Copy link

niedzielski commented Mar 27, 2020

I'm a newbie and this issue also got me using an optional unit struct:

use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Serialize, PartialEq)]
struct Player;

#[derive(Deserialize, Debug, Serialize, PartialEq)]
struct Components {
    player: Option<Player>
}

fn main() {
    let player = Player {};
    let player_str = &serde_json::to_string(&player).unwrap();
    println!("{}", player_str); // `null`, I was thinking an empty object but ok.
    let player_deser: Player = serde_json::from_str(player_str).unwrap();
    println!("{:?}", &player_deser); // `Player`, that's good.
    assert_eq!(player_deser, player);

    let components = Components {player: Some(player)};
    let components_str = &serde_json::to_string(&components).unwrap();
    println!("{}", components_str); // `{"player":null}`, I'm not sure how to specify None but ok.
    let components_deser: Components = serde_json::from_str(components_str).unwrap();
    println!("{:?}", &components_deser); // `Components { player: None }`, oh no! The issue is that I can't specify Some!

    // The next line fails:
    //     thread 'main' panicked at 'assertion failed: `(left == right)`
    //       left: `Components { player: Some(Player) }`,
    //      right: `Components { player: None }`', src/main.rs:29:5
    assert_eq!(components, components_deser);
}

Playground

Be aware that neither serde nor serde_json makes round trip guarantees in general.

There may be very practical reasons for this but I was especially surprised to read it as I normally verify serialization and deserialization by testing that an arbitrary input makes the roundtrip. E.g., input == deserialize(serialize(input)) in other languages.

Thank you contributors for your continued hard work on this library! It's great 👍 💯

Edit (2020-03-31): I stumbled upon a more relevant example from a related Rust de/serialization library that does support roundtrips, bincode.

@dtolnay dtolnay changed the title Option<()> value doesn't round tip through JSON Option<()> value doesn't round trip through JSON Jul 5, 2020
@dtolnay dtolnay closed this as completed Jul 5, 2020
@serde-rs serde-rs locked and limited conversation to collaborators May 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

3 participants