Skip to content

Commit 0c68703

Browse files
committed
dumps(), loads()
1 parent 189ced9 commit 0c68703

4 files changed

Lines changed: 315 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ name = "orjson"
1212
crate-type = ["cdylib"]
1313

1414
[dependencies]
15-
chrono = { version = "0.4", features = ["serde"] }
1615
pyo3 = { path = "./pyo3", features = ["extension-module", "python3"]}
1716
serde = { version = "1" }
1817
serde_json = { version = "1" }

src/decode.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
2+
3+
use std::fmt;
4+
use std::marker::PhantomData;
5+
use std::borrow::Cow;
6+
use smallvec::SmallVec;
7+
use pyo3::prelude::*;
8+
use pyo3::types::*;
9+
use serde::de::{self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
10+
11+
import_exception!(json, JSONDecodeError);
12+
13+
pub fn deserialize(py: Python, data: &[u8]) -> PyResult<PyObject> {
14+
let seed = JsonValue::new(py);
15+
let mut deserializer = serde_json::Deserializer::from_slice(data);
16+
match seed.deserialize(&mut deserializer) {
17+
Ok(py_object) => {
18+
deserializer
19+
.end()
20+
.map_err(|e| JSONDecodeError::py_err((e.to_string(), "", 0)))?;
21+
Ok(py_object)
22+
}
23+
Err(e) => {
24+
return Err(JSONDecodeError::py_err((e.to_string(), "", 0)));
25+
}
26+
}
27+
}
28+
29+
#[derive(Clone)]
30+
struct JsonValue<'a> {
31+
py: Python<'a>,
32+
}
33+
34+
impl<'a> JsonValue<'a> {
35+
36+
fn new(py: Python<'a>) -> JsonValue<'a> {
37+
JsonValue { py }
38+
}
39+
}
40+
41+
impl<'de, 'a> DeserializeSeed<'de> for JsonValue<'a> {
42+
43+
type Value = PyObject;
44+
45+
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
46+
where
47+
D: Deserializer<'de>,
48+
{
49+
deserializer.deserialize_any(self)
50+
}
51+
}
52+
53+
impl<'de, 'a> Visitor<'de> for JsonValue<'a> {
54+
55+
type Value = PyObject;
56+
57+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
58+
formatter.write_str("JSON")
59+
}
60+
61+
fn visit_unit<E>(self) -> Result<Self::Value, E> {
62+
Ok(self.py.None())
63+
}
64+
65+
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
66+
where
67+
E: de::Error,
68+
69+
{
70+
Ok(value.to_object(self.py))
71+
}
72+
73+
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
74+
where
75+
E: de::Error,
76+
{
77+
Ok(value.to_object(self.py))
78+
}
79+
80+
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
81+
where
82+
E: de::Error,
83+
{
84+
Ok(value.to_object(self.py))
85+
}
86+
87+
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
88+
where
89+
E: de::Error,
90+
{
91+
Ok(value.to_object(self.py))
92+
}
93+
94+
fn visit_borrowed_str<E>(self, value: &str) -> Result<Self::Value, E>
95+
where
96+
E: de::Error,
97+
{
98+
Ok(value.to_object(self.py))
99+
}
100+
101+
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
102+
where
103+
E: de::Error,
104+
{
105+
Ok(value.to_object(self.py))
106+
}
107+
108+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
109+
where
110+
A: SeqAccess<'de>,
111+
{
112+
let mut elements: SmallVec<[PyObject; 8]> = SmallVec::new();
113+
while let Some(elem) = seq.next_element_seed(self.clone())? {
114+
elements.push(elem);
115+
}
116+
Ok(elements.as_slice().to_object(self.py))
117+
}
118+
119+
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
120+
where
121+
A: MapAccess<'de>,
122+
{
123+
let mut elements: SmallVec<[(PyObject, PyObject); 8]> = SmallVec::new();
124+
while let Some((key, value)) = map.next_entry_seed(PhantomData::<Cow<str>>, self.clone())? {
125+
elements.push((key.to_object(self.py), value));
126+
}
127+
let dict = PyDict::new(self.py);
128+
for (key, value) in elements.iter() {
129+
dict.set_item(key, value).unwrap()
130+
}
131+
Ok(dict.into())
132+
}
133+
}

src/encode.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
2+
3+
use pyo3::prelude::*;
4+
use pyo3::types::*;
5+
use serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer};
6+
7+
pub fn serialize(py: Python, obj: PyObject) -> PyResult<PyObject> {
8+
let typerefs = TypeRefs::new(py);
9+
let s: Result<Vec<u8>, JsonError> = serde_json::to_vec(
10+
&SerializePyObject { py: py, refs: &typerefs, obj: obj.as_ref(py) }
11+
).map_err(|error| JsonError::InvalidConversion { error });
12+
Ok(PyBytes::new(py, (s?).as_slice()).into())
13+
}
14+
15+
pub enum JsonError {
16+
InvalidConversion { error: serde_json::Error },
17+
}
18+
19+
impl From<JsonError> for PyErr {
20+
21+
fn from(h: JsonError) -> PyErr {
22+
match h {
23+
JsonError::InvalidConversion { error } => {
24+
PyErr::new::<pyo3::exceptions::TypeError, _>(error.to_string())
25+
}
26+
}
27+
}
28+
}
29+
30+
#[derive(Clone)]
31+
pub struct TypeRefs {
32+
pub str: *mut pyo3::ffi::PyTypeObject,
33+
pub bytes: *mut pyo3::ffi::PyTypeObject,
34+
pub dict: *mut pyo3::ffi::PyTypeObject,
35+
pub list: *mut pyo3::ffi::PyTypeObject,
36+
pub tuple: *mut pyo3::ffi::PyTypeObject,
37+
pub none: *mut pyo3::ffi::PyTypeObject,
38+
pub bool: *mut pyo3::ffi::PyTypeObject,
39+
pub int: *mut pyo3::ffi::PyTypeObject,
40+
pub float: *mut pyo3::ffi::PyTypeObject,
41+
}
42+
43+
impl TypeRefs {
44+
45+
pub fn new(py: Python) -> TypeRefs {
46+
TypeRefs {
47+
str: PyUnicode::new(py, "python").as_ref(py).get_type_ptr(),
48+
bytes: PyBytes::new(py, b"python").as_ref(py).get_type_ptr(),
49+
dict: PyDict::new(py).as_ref().get_type_ptr(),
50+
list: PyList::empty(py).as_ref().get_type_ptr(),
51+
tuple: PyTuple::empty(py).as_ref(py).get_type_ptr(),
52+
none: py.None().as_ref(py).get_type_ptr(),
53+
bool: true.to_object(py).as_ref(py).get_type_ptr(),
54+
int: 1.to_object(py).as_ref(py).get_type_ptr(),
55+
float: 1.0.to_object(py).as_ref(py).get_type_ptr(),
56+
}
57+
}
58+
}
59+
60+
pub struct SerializePyObject<'p, 'a> {
61+
pub py: Python<'p>,
62+
pub refs: &'a TypeRefs,
63+
pub obj: &'a PyObjectRef,
64+
}
65+
66+
impl<'p, 'a> Serialize for SerializePyObject<'p, 'a> {
67+
68+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69+
where
70+
S: Serializer,
71+
{
72+
let obj_ptr = self.obj.get_type_ptr();
73+
if obj_ptr == self.refs.str {
74+
let val: &PyUnicode = self.obj.extract().unwrap();
75+
serializer.serialize_str(
76+
unsafe { std::str::from_utf8_unchecked(val.as_bytes()) }
77+
)
78+
} else if obj_ptr == self.refs.bytes {
79+
let val: &PyBytes = self.obj.extract().unwrap();
80+
serializer.serialize_str(
81+
unsafe { std::str::from_utf8_unchecked(val.as_bytes()) }
82+
)
83+
} else if obj_ptr == self.refs.dict {
84+
let val: &PyDict = self.obj.extract().unwrap();
85+
let len = val.len();
86+
if len != 0 {
87+
let mut map = serializer.serialize_map(Some(len))?;
88+
for (key, value) in val.iter() {
89+
map.serialize_entry(
90+
&SerializePyObject { py: self.py, refs: self.refs, obj: key },
91+
&SerializePyObject { py: self.py, refs: self.refs, obj: value },
92+
)?;
93+
}
94+
map.end()
95+
} else {
96+
serializer.serialize_map(None).unwrap().end()
97+
}
98+
} else if obj_ptr == self.refs.list {
99+
let val: &PyList = self.obj.extract().unwrap();
100+
let len = val.len();
101+
if len != 0 {
102+
let mut seq = serializer.serialize_seq(Some(len))?;
103+
for element in val {
104+
seq.serialize_element(
105+
&SerializePyObject { py: self.py, refs: self.refs, obj: element })?
106+
}
107+
seq.end()
108+
} else {
109+
serializer.serialize_seq(None).unwrap().end()
110+
}
111+
} else if obj_ptr == self.refs.tuple {
112+
let val: &PyTuple = self.obj.extract().unwrap();
113+
let len = val.len();
114+
if len != 0 {
115+
let mut seq = serializer.serialize_seq(Some(len))?;
116+
for element in val {
117+
seq.serialize_element(
118+
&SerializePyObject { py: self.py, refs: self.refs, obj: element }
119+
)?
120+
}
121+
seq.end()
122+
} else {
123+
serializer.serialize_seq(None).unwrap().end()
124+
}
125+
} else if obj_ptr == self.refs.bool {
126+
let val: &PyBool = self.obj.extract().unwrap();
127+
serializer.serialize_bool(val.is_true())
128+
} else if obj_ptr == self.refs.int {
129+
if let Ok(val) = <i64 as FromPyObject>::extract(self.obj) {
130+
serializer.serialize_i64(val)
131+
} else {
132+
Err(ser::Error::custom(
133+
format_args!("Integer exceeds 64-bit max: {:?}", self.obj)
134+
))
135+
}
136+
} else if obj_ptr == self.refs.float {
137+
let val: &PyFloat = self.obj.extract().unwrap();
138+
serializer.serialize_f64(val.value())
139+
} else if obj_ptr == self.refs.none {
140+
serializer.serialize_unit()
141+
} else {
142+
Err(ser::Error::custom(format_args!(
143+
"Type is not JSON serializable: {}",
144+
self.obj.get_type().name(),
145+
)))
146+
}
147+
}
148+
}

src/lib.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,43 @@
55
#[macro_use]
66
extern crate pyo3;
77

8+
extern crate serde;
9+
extern crate serde_json;
10+
extern crate smallvec;
11+
812
use pyo3::prelude::*;
13+
use pyo3::types::*;
14+
15+
mod encode;
16+
mod decode;
917

1018
#[pymodule]
11-
fn orjson(_: Python, m: &PyModule) -> PyResult<()> {
19+
fn orjson(py: Python, m: &PyModule) -> PyResult<()> {
1220
m.add("__version__", env!("CARGO_PKG_VERSION"))?;
21+
m.add_wrapped(wrap_function!(dumps))?;
22+
m.add_wrapped(wrap_function!(loads))?;
23+
m.add("JSONDecodeError", py.get_type::<decode::JSONDecodeError>())?;
1324
Ok(())
1425
}
26+
27+
#[pyfunction]
28+
pub fn loads(py: Python, obj: PyObject) -> PyResult<PyObject> {
29+
let obj_ref = obj.as_ref(py);
30+
if let Ok(val) = <PyUnicode as PyTryFrom>::try_from(obj_ref) {
31+
decode::deserialize(py, val.as_bytes())
32+
} else if let Ok(val) = <PyBytes as PyTryFrom>::try_from(obj_ref) {
33+
decode::deserialize(py, val.as_bytes())
34+
}
35+
else {
36+
return Err(
37+
pyo3::exceptions::TypeError::py_err(
38+
format!("Input must be unicode or bytes, not: {}", obj_ref.get_type().name())
39+
)
40+
);
41+
}
42+
}
43+
44+
#[pyfunction]
45+
pub fn dumps(py: Python, obj: PyObject) -> PyResult<PyObject> {
46+
encode::serialize(py, obj)
47+
}

0 commit comments

Comments
 (0)