Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created August 9, 2021 13:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rust-play/e853c2dee04e6b47b01f98b4ae634615 to your computer and use it in GitHub Desktop.
Save rust-play/e853c2dee04e6b47b01f98b4ae634615 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
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