3

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).

4

2 回答 2

1

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));
}
于 2017-07-21T18:51:17.593 回答
0

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.

于 2017-07-21T20:36:17.270 回答