-
-
Save rust-play/e853c2dee04e6b47b01f98b4ae634615 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use serde::{Deserialize, Deserializer}; | |
use serde_json::json; | |
#[derive(Debug, Deserialize, PartialEq, Eq)] | |
struct Account { | |
id: usize, | |
} | |
#[derive(Debug, Deserialize, PartialEq, Eq)] | |
struct Session { | |
id: String, | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
struct Response { | |
accounts: Vec<Account>, | |
session: Session, | |
} | |
impl<'de> Deserialize<'de> for Response { | |
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | |
where | |
D: Deserializer<'de>, | |
{ | |
use serde::de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor}; | |
#[derive(Deserialize)] | |
#[serde(field_identifier, rename_all = "PascalCase")] | |
enum Field { | |
Account, | |
Session, | |
} | |
#[derive(Default)] | |
struct Temporary { | |
accounts: Vec<Account>, | |
session: Option<Session>, | |
} | |
struct TemporaryWalk<'a>(&'a mut Temporary); | |
// Implement `DeserializeSeed` so every partial object in the array | |
// can be appended to the Temporary struct directly. | |
impl<'a, 'de> DeserializeSeed<'de> for TemporaryWalk<'a> { | |
type Value = (); | |
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> | |
where | |
D: Deserializer<'de>, | |
{ | |
struct TemporaryVisitor<'a>(&'a mut Temporary); | |
impl<'de, 'a> Visitor<'de> for TemporaryVisitor<'a> { | |
type Value = (); | |
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { | |
formatter.write_str("temporary map") | |
} | |
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> | |
where | |
A: MapAccess<'de>, | |
{ | |
while let Some(key) = map.next_key()? { | |
match key { | |
Field::Account => self.0.accounts.push(map.next_value()?), | |
Field::Session => { | |
if self.0.session.is_some() { | |
return Err(Error::duplicate_field("session")); | |
} | |
self.0.session = Some(map.next_value()?) | |
} | |
} | |
} | |
Ok(()) | |
} | |
} | |
deserializer.deserialize_map(TemporaryVisitor(self.0)) | |
} | |
} | |
struct ResponseVisitor; | |
impl<'de> Visitor<'de> for ResponseVisitor { | |
type Value = Response; | |
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { | |
formatter.write_str("struct Response") | |
} | |
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> | |
where | |
A: SeqAccess<'de>, | |
{ | |
// Pass around a temporary struct with empty default values | |
// which is then built incrementally. | |
let mut temporary = Temporary::default(); | |
// From https://docs.serde.rs/serde/de/trait.DeserializeSeed.html | |
while let Some(()) = seq.next_element_seed(TemporaryWalk(&mut temporary))? { | |
// Do nothing | |
} | |
// From https://serde.rs/deserialize-struct.html | |
let accounts = temporary.accounts; | |
let session = temporary | |
.session | |
.ok_or_else(|| Error::missing_field("session"))?; | |
Ok(Response { accounts, session }) | |
} | |
} | |
deserializer.deserialize_seq(ResponseVisitor) | |
} | |
} | |
fn main() -> anyhow::Result<()> { | |
let value = json!([ | |
{ | |
"Account": { | |
"id": 0, | |
} | |
}, | |
{ | |
"Account": { | |
"id": 1, | |
} | |
}, | |
{ | |
"Session": { | |
"id": "hello!" | |
} | |
}, | |
]); | |
let response: Response = serde_json::from_value(value)?; | |
dbg!(response); | |
Ok(()) | |
} | |
#[test] | |
fn test() { | |
let value = json!([ | |
{ | |
"Account": { | |
"id": 0, | |
} | |
}, | |
{ | |
"Account": { | |
"id": 1, | |
} | |
}, | |
{ | |
"Session": { | |
"id": "hello!" | |
} | |
}, | |
]); | |
let response: Response = serde_json::from_value(value).unwrap(); | |
assert_eq!( | |
response, | |
Response { | |
accounts: vec![Account { id: 0 }, Account { id: 1 },], | |
session: Session { | |
id: "hello!".to_string() | |
} | |
} | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment