Categories
Tags
algorithms APIT arm assembly asynchronous base64 Blogging box c clang-format cmake compiler concurrency const_fn contravariant cos covariant cpp Customization cybersecurity DataStructure db Demo deserialization discrete doc DP Dynamic Example FFI flat_map FP Functional functions futures Fuwari GATs gccrs generics gitignore GUI hacking hashmap haskell heap interop invariant iterator justfile kernel LaTeX LFU linux MachineLearning Markdown math ML OnceLock optimization OS parallels perf physics pin postgresql release RPIT rust science Science serialization shift sin SmallProjects std String surrealdb swisstable synchronous tan traits triangulation utf16 utf8 Video x86_64 xilem zig
651 words
3 minutes
260110_json_serde_deserde_std_impl_Small_Projects
link
Summary
- std만 이용해서 나만의 serde_json을 만들어보자.
- HashMap스타일로 키값과 value가 나오는 스타일로 만듬.
- 많이 허접하지만 기본 원리를 익힌다 생각하자.
Rust
json 샘플파일
lib.rs
#[derive(Debug)]
pub struct RawJsonSerde {
pub title: String,
pub value: String,
}
#[derive(Debug)]
pub struct RawJsonDeserde {
pub title: String,
pub value: String,
}
// Simple JSON serializer using only std
impl RawJsonSerde {
pub fn to_json(&self) -> String {
format!(
r#"{{"title":"{}","value":"{}"}}"#,
escape_json_string(&self.title),
escape_json_string(&self.value)
)
}
}
// Simple JSON deserializer using only std
impl RawJsonDeserde {
pub fn from_json(json: &str) -> Result<Self, String> {
let json = json.trim();
if !json.starts_with('{') || !json.ends_with('}') {
return Err("Invalid JSON: missing braces".to_string());
}
let content = &json[1..json.len() - 1];
let mut title = String::new();
let mut value = String::new();
// Parse title field: look for "title":"value"
if let Some(start) = content.find(r#""title":"#) {
let value_start = start + 8; // Skip "title": (8 chars: quote + title + quote + colon)
// Skip any whitespace after the colon
let after_whitespace = &content[value_start..].trim_start();
// Skip the opening quote of the value
let after_opening_quote = &after_whitespace[1..];
if let Some(end) = find_json_string_end(after_opening_quote) {
title = unescape_json_string(&after_opening_quote[..end]);
}
}
// Parse value field: look for "value":"data" or "body":"data"
let value_pattern = if content.contains(r#""value":"#) {
r#""value":"#
} else if content.contains(r#""body":"#) {
r#""body":"#
} else {
""
};
if !value_pattern.is_empty() {
if let Some(start) = content.find(value_pattern) {
let value_start = start + value_pattern.len(); // Skip "value":" or "body":"
// Skip any whitespace after the colon
let after_whitespace = &content[value_start..].trim_start();
// Skip the opening quote of the value
let after_opening_quote = &after_whitespace[1..];
if let Some(end) = find_json_string_end(after_opening_quote) {
value = unescape_json_string(&after_opening_quote[..end]);
}
}
}
if title.is_empty() && value.is_empty() {
return Err("Invalid JSON: missing fields".to_string());
}
Ok(RawJsonDeserde { title, value })
}
}
// Helper: Escape special characters in JSON strings
fn escape_json_string(s: &str) -> String {
let mut result = String::with_capacity(s.len());
for c in s.chars() {
match c {
'\\' => result.push_str("\\\\"),
'"' => result.push_str("\\\""),
'\n' => result.push_str("\\n"),
'\r' => result.push_str("\\r"),
'\t' => result.push_str("\\t"),
_ => result.push(c),
}
}
result
}
// Helper: Find the end of a JSON string (handling escaped quotes)
// Input should NOT include the opening quote
fn find_json_string_end(s: &str) -> Option<usize> {
let mut chars = s.chars().peekable();
let mut pos = 0;
while let Some(c) = chars.next() {
match c {
'\\' => {
// Skip next character (escape sequence)
pos += c.len_utf8();
if let Some(escaped_char) = chars.next() {
pos += escaped_char.len_utf8();
}
}
'"' => return Some(pos),
_ => {
pos += c.len_utf8();
}
}
}
None
}
// Helper: Unescape JSON string
fn unescape_json_string(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
if let Some(next) = chars.next() {
match next {
'\\' => result.push('\\'),
'"' => result.push('"'),
'n' => result.push('\n'),
'r' => result.push('\r'),
't' => result.push('\t'),
_ => {
result.push(c);
result.push(next);
}
}
}
} else {
result.push(c);
}
}
result
}
- test코드
use json_serde_impl::*;
#[test]
fn serde_data() {
let raw_json = RawJsonSerde {
title: "Hello \"World\"".to_string(),
value: "Test\\Value".to_string(),
};
let json_string = raw_json.to_json();
assert!(json_string.contains("title"));
assert!(json_string.contains("value"));
let desered = RawJsonDeserde::from_json(&json_string).unwrap();
assert_eq!(desered.title, "Hello \"World\"");
assert_eq!(desered.value, "Test\\Value");
}
#[test]
fn serde_simple() {
let data = RawJsonSerde {
title: "Test Title".to_string(),
value: "Test Value".to_string(),
};
let json = data.to_json();
let parsed = RawJsonDeserde::from_json(&json).unwrap();
assert_eq!(parsed.title, "Test Title");
assert_eq!(parsed.value, "Test Value");
}
#[test]
fn deserde_invalid_json() {
let result = RawJsonDeserde::from_json("not json");
assert!(result.is_err());
}
#[test]
fn input_json() {
let json_data = include_str!("../assets/input.json");
let parsed = RawJsonDeserde::from_json(&json_data).unwrap();
println!("{parsed:#?}");
}- assets JSON파일
{
"userId": 1,
"id": 10,
"title": "optio molestias id quia eum",
"body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error"
}
260110_json_serde_deserde_std_impl_Small_Projects
https://younghakim7.github.io/blog/posts/260110_json_serde_deserde_std_impl_small_projects/