json icon indicating copy to clipboard operation
json copied to clipboard

Numeric values for internally tagged enum discriminator

Open jonhoo opened this issue 3 years ago • 1 comments

I'm working with a JSON type that is internally tagged with a version number. My first instinct was to write is like so:

use serde_derive::Deserialize;

#[derive(Deserialize)]
#[serde(tag = "_version")]
enum Versioned {
    #[serde(rename = "1")]
    V1 {
        works: bool,
    }
}

This works fine if the version number is represented as a string:

#[test]
fn is_str() {
    let v: Versioned = serde_json::from_str(r#"{"_version": "1", "works": true}"#).unwrap();
    assert!(matches!(v, Versioned::V1 { works: true }));
}

But fails if the version number is encoded as a number (as it indeed is in my case):

#[test]
fn is_number() {
    let v: Versioned = serde_json::from_str(r#"{"_version": 1, "works": true}"#).unwrap();
    assert!(matches!(v, Versioned::V1 { works: true }));
}

Specifically, serde_json errors (playground) with

Error("invalid type: integer `1`, expected variant identifier", line: 1, column: 14)

This feels like a bug/missing feature, in that serde should be willing to visit a number if the name of the variant is itself a string of all digits. Alternatively, I suppose I could implement my own Deserialize for this type, but that feels like overkill. Is there some other way to achieve this by sprinkling some more magical attributes?

jonhoo avatar Feb 27 '21 18:02 jonhoo

I'm pretty sure the issue stems from somewhere deep in serde-derive around here, but I don't know enough of serde's internals to debug it properly. I think we'd need to inject a numerical visitor in here somehow, but not sure how that interacts with the collect_other_fields bit. I suppose this is really a bug in serde-derive and not in serde_json. Could this issue perhaps be moved to there by someone with permissions?

jonhoo avatar Feb 27 '21 19:02 jonhoo