0

I'm trying to store the value of a field in a global context, but this doesn't work when the JSON object is in the wrong order.

How can I make it so the output is always, a then b?

Playground

use serde::{Deserialize, Deserializer};
use std::error::Error;

fn a_deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    let num = Deserialize::deserialize(deserializer)?;

    println!("a");

    Ok(num)
}

fn b_deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    let num = Deserialize::deserialize(deserializer)?;

    println!("b");

    Ok(num)
}

#[derive(Deserialize)]
struct Test {
    #[serde(deserialize_with = "a_deserialize")]
    a: u32,
    #[serde(deserialize_with = "b_deserialize")]
    b: u32,
}

fn main() -> Result<(), Box<dyn Error>> {
    // notice the order is b then a
    let str = "{ \"b\": 1, \"a\": 2 }";

    let test: Test = serde_json::from_str(&str)?;
    // it prints out b then a, but I want a then b -- the same as in the struct definition

    Ok(())
}
E_net4
  • 27,810
  • 13
  • 101
  • 139
Sam Denty
  • 3,693
  • 3
  • 30
  • 43
  • 1
    It's not entirely clear why you would do that — in theory, the deserialization process in itself should be relatively pure (in the sense that it doesn't have side effects) — so I'm not sure serde offers such a functionality. – jthulhu Aug 25 '22 at 11:25

1 Answers1

3

Serde was designed to incorporate a stateful deserializer which processes the various items in sequence, as the source data is consumed. As such, it is unrealistic to tell this deserializer to deserialize content in a different order. Given the JSON object

{"b": 1, "a": 2}

b appears first, so that will be processed first. Anything else would have required the deserializer to keep track of potentially all data read in the past, keeping it "on hold" to visit it again at a later stage, making it more inefficient.

It is more realistic and feasible to adjust your program accordingly: visit the fields of a struct after it has been fully deserialized.

#[derive(Deserialize)]
struct Test {
    a: u32,
    b: u32,
}

fn main() -> Result<(), Box<dyn Error>> {
    let str = "{ \"b\": 1, \"a\": 2 }";

    let test: Test = serde_json::from_str(&str)?;
    
    println!("a: {}", test.a);
    println!("b: {}", test.b);

    Ok(())
}

See also:

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • I agree that changing the order would be unrealistic in this situation, but as a nit: The deserializer must keep some information on hold for internally tagged enums, if the tag isn't the first entry. (Though I have no idea how that's internally implemented. Most likely, it just seeks forward, only keeping a pointer to the struct start.) – Caesar Aug 25 '22 at 12:35
  • @Caesar Updated the answer a bit. Yes, there is always some state, but changing the order would bring that state to a whole other magnitude. :) – E_net4 Aug 25 '22 at 13:23