Reputation: 6862
I want to convert 3rd party library enums to and from JSON. As I don't want to edit the 3rd party source code I don't want to use the derive macros.
I want to handwrite the serde_json deserialize
method. I am thinking that pattern matching is the way to go but the things I need to match on are not public:
extern crate serde_json;
#[test]
fn test_parse_from_json() {
let s = r#"{
"e": 0,
"c": 1,
"n": 2
}"#;
let v: Map<String, Value> = serde_json::from_str(&s).unwrap();
match (&v["e"], &v["c"], &v["n"]) {
(&Value::Number(ref e), &Value::Number(ref c), &Value::Number(ref n)) => {
// e is a PosInt(u64) but that isn't public to match one nor access its methods!
let value = e.n; // error[E0616]: field `n` of struct `serde_json::Number` is private
}
_ => (),
}
}
That doesn't compile. If I replace that inner bit with something I can set a breakpoint on, I can see in the debugger that e
is a Number
which contains a PosInt(0)
.
Upvotes: 3
Views: 2746
Reputation: 6862
Based on @Shepmaster's answer I got a lot further along:
let v: Map<String, Value> = serde_json::from_str(&s).unwrap();
let bn: Option<BallotNumber>;
match (&v["e"],&v["c"],&v["n"]) {
(&Value::Number(ref e),&Value::Number(ref c),&Value::Number(ref n)) => {
match ( &e.as_u64(), &c.as_u64(), &n.as_u64() ) {
(&Some(era), &Some(count), &Some(number)) =>
bn = Some(BallotNumber::new(era, count, number)),
_ =>
bn = None
}
},
_ =>
bn = None
}
That does the trick only it looks like a bit of a train smash. Given that @Shepmaster's answer points out that serde provided a way around the Orphan rule I will use that approach.
Upvotes: 0
Reputation: 431479
You cannot pattern match on private fields because they are private. You have to use the accessors the library decides to provide. The serde_json documentation for Number
shows that it has methods like as_u64
:
let value = e.as_u64().expect("not a u64");
As I don't want to edit the 3rd party source code I don't want to use the derive macros.
You may be suffering from the X-Y problem. For example, the Serde docs describe how to implement the traits for a "remote type".
You could also create your own type that you deserialize into and construct a transformation to and from the library type:
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[derive(Deserialize)]
struct Mine {
e: u64,
c: u64,
n: u64,
}
#[test]
fn test_parse_from_json() {
let s = r#"{
"e": 0,
"c": 1,
"n": 2
}"#;
let v: Mine = serde_json::from_str(&s).unwrap();
println!("{:?}", (v.e, v.c, v.n));
}
Upvotes: 1