Skip to main content

co_primitives/library/
json.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use ipld_core::{ipld::Ipld, serde::to_ipld};
5use serde::{de::DeserializeOwned, Deserialize, Serialize};
6
7/// Serialize `value` to JSON string (using dag-json).
8pub fn to_json<T: Serialize>(value: &T) -> Result<Vec<u8>, JsonError> {
9	let ipld =
10		to_ipld(value).map_err(|err| JsonError::Serialize(std::any::type_name::<T>().to_owned(), err.to_string()))?;
11	serde_ipld_dagjson::to_vec(&ipld)
12		.map_err(|err| JsonError::Serialize(std::any::type_name::<T>().to_owned(), err.to_string()))
13}
14
15/// Deserialize from JSON string (using dag-json).
16pub fn from_json<'a, T: Deserialize<'a>>(value: &'a [u8]) -> Result<T, JsonError> {
17	// because of an bug in `serde_ipld_dagjson` we take an extra step via Ipld to make bytes work correctly
18	let ipld: Ipld = serde_ipld_dagjson::from_slice(value)
19		.map_err(|err| JsonError::Deserialize(std::any::type_name::<T>().to_owned(), err.to_string()))?;
20	T::deserialize(ipld).map_err(|err| JsonError::Deserialize(std::any::type_name::<T>().to_owned(), err.to_string()))
21}
22
23/// Serialize `value` to JSON string (using dag-json).
24pub fn to_json_string<T: Serialize>(value: &T) -> Result<String, JsonError> {
25	String::from_utf8(to_json(value)?)
26		.map_err(|err| JsonError::Serialize(std::any::type_name::<T>().to_owned(), err.to_string()))
27}
28
29/// Deserialize from JSON string (using dag-json).
30pub fn from_json_string<T: DeserializeOwned>(value: impl AsRef<[u8]>) -> Result<T, JsonError> {
31	from_json(value.as_ref())
32		.map_err(|err| JsonError::Deserialize(std::any::type_name::<T>().to_owned(), err.to_string()))
33}
34
35#[derive(Debug, thiserror::Error)]
36pub enum JsonError {
37	#[error("Serialize {0} to JSON failed: {1}")]
38	Serialize(String, String),
39
40	#[error("Deserialize {0} from JSON failed: {1}")]
41	Deserialize(String, String),
42}
43
44#[cfg(test)]
45mod tests {
46	use crate::{from_json, to_json};
47	use serde::{Deserialize, Serialize};
48
49	#[derive(Debug, Serialize, Deserialize, PartialEq)]
50	struct Test {
51		#[serde(with = "serde_bytes")]
52		hello: Vec<u8>,
53	}
54
55	#[test]
56	fn test_bytes() {
57		let payload = Test { hello: "world".as_bytes().to_vec() };
58		let json = to_json(&payload).unwrap();
59		assert_eq!(std::str::from_utf8(&json).unwrap(), "{\"hello\":{\"/\":{\"bytes\":\"d29ybGQ\"}}}");
60		let deserialized: Test = from_json(&json).unwrap();
61		assert_eq!(deserialized, payload);
62	}
63}