Skip to content

Instantly share code, notes, and snippets.

@tiqwab
Last active March 18, 2022 09:36
Show Gist options
  • Save tiqwab/c4b9edf6638eeb0d96aea736dbaff18e to your computer and use it in GitHub Desktop.
Save tiqwab/c4b9edf6638eeb0d96aea736dbaff18e to your computer and use it in GitHub Desktop.
serde まわりのノウハウをまとめたい
// enum の Serialize, Deserialize with tag
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct Novel {
author: String,
pages: u32,
}
impl Novel {
pub fn new(author: &str, pages: u32) -> Novel {
Novel {
author: author.to_string(),
pages,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Diary {
volume: u32,
pages: u32,
}
impl Diary {
pub fn new(volume: u32, pages: u32) -> Diary {
Diary { volume, pages }
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum Book {
#[serde(rename = "novel")]
Novel(Novel),
#[serde(rename = "diary")]
Diary(Diary),
}
// Ok(Novel(Novel { author: "Alice", pages: 200 }))
// Ok(Diary(Diary { volume: 2, pages: 40 }))
// {"type":"novel","author":"Alice","pages":200}
fn main() {
use custom_enum::*;
let json1 = r#"{"type": "novel", "author": "Alice", "pages": 200}"#;
let json2 = r#"{"type": "diary", "volume": 2, "pages": 40}"#;
println!("{:?}", serde_json::from_str::<Book>(json1));
println!("{:?}", serde_json::from_str::<Book>(json2));
let book = Book::Novel(Novel::new("Alice", 200));
println!("{}", serde_json::to_string(&book).unwrap());
}
// 既に定義済みの Serialize, Deserialize を利用して新たなそれらを定義する例
// serde のドキュメントを小一時間眺めれば簡単な Serialize, Deserialize は一からでも書けるかもしれないが、
// 特に deserialize 側はけっこう難しいと思う。
//
// 一例としてメールアドレスを扱うために String をラップした構造体に対して定義してみる
use lazy_static::lazy_static;
use regex::Regex;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
lazy_static! {
// This refers to the validation pattern of input tag by Web browser
// ref. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email
static ref REGEX_PATTERN: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$").unwrap();
}
#[derive(Debug)]
pub struct MailAddress(String);
impl MailAddress {
pub fn new(s: &str) -> Result<MailAddress, String> {
if REGEX_PATTERN.is_match(s) {
Ok(MailAddress(s.to_string()))
} else {
Err("expected mail address".to_string())
}
}
}
impl Serialize for MailAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
String::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for MailAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
MailAddress::new(&s).map_err(|err_msg| D::Error::custom(err_msg))
}
}
// Output:
// Ok(Foo { mail_address: MailAddress("foo@example.com") })
// Err(Error("expected mail address", line: 1, column: 36))
// {"mail_address":"foo@example.com"}
fn main() {
use custom_string::*;
#[derive(Debug, Deserialize, Serialize)]
struct Foo {
mail_address: MailAddress,
}
let json1 = r#"{"mail_address": "foo@example.com"}"#;
let json2 = r#"{"mail_address": "foo@.example.com"}"#;
println!("{:?}", serde_json::from_str::<Foo>(json1));
println!("{:?}", serde_json::from_str::<Foo>(json2));
let foo = Foo {
mail_address: MailAddress::new("foo@example.com").unwrap(),
};
println!("{}", serde_json::to_string(&foo).unwrap());
}
@tiqwab
Copy link
Author

tiqwab commented Mar 18, 2022

出力:

Ok(Foo { mail_address: MailAddress("foo@example.com") })
Err(Error("expected mail address", line: 1, column: 36))
{"mail_address":"foo@example.com"}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment