mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #113 from OddBloke/json
Rework json.dumps to support subclasses
This commit is contained in:
@@ -11,12 +11,19 @@ assert "1" == json.dumps(1)
|
||||
assert "1.0" == json.dumps(1.0)
|
||||
assert "true" == json.dumps(True)
|
||||
assert "false" == json.dumps(False)
|
||||
assert 'null' == json.dumps(None)
|
||||
|
||||
assert '[]' == json.dumps([])
|
||||
assert '[1]' == json.dumps([1])
|
||||
assert '[[1]]' == json.dumps([[1]])
|
||||
round_trip_test([1, "string", 1.0, True])
|
||||
|
||||
assert '[]' == json.dumps(())
|
||||
assert '[1]' == json.dumps((1,))
|
||||
assert '[[1]]' == json.dumps(((1,),))
|
||||
# tuples don't round-trip through json
|
||||
assert [1, "string", 1.0, True] == json.loads(json.dumps((1, "string", 1.0, True)))
|
||||
|
||||
assert '{}' == json.dumps({})
|
||||
# TODO: uncomment once dict comparison is implemented
|
||||
# round_trip_test({'a': 'b'})
|
||||
@@ -35,3 +42,17 @@ assert False == json.loads('false')
|
||||
assert [] == json.loads('[]')
|
||||
assert ['a'] == json.loads('["a"]')
|
||||
assert [['a'], 'b'] == json.loads('[["a"], "b"]')
|
||||
|
||||
class String(str): pass
|
||||
|
||||
assert '"string"' == json.dumps(String("string"))
|
||||
|
||||
# TODO: Uncomment and test once int/float construction is supported
|
||||
# class Int(int): pass
|
||||
# class Float(float): pass
|
||||
|
||||
# TODO: Uncomment and test once sequence/dict subclasses are supported by
|
||||
# json.dumps
|
||||
# class List(list): pass
|
||||
# class Tuple(tuple): pass
|
||||
# class Dict(dict): pass
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn new(dict_type: PyObjectRef) -> PyObjectRef {
|
||||
)
|
||||
}
|
||||
|
||||
fn get_elements(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
|
||||
pub fn get_elements(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
|
||||
if let PyObjectKind::Dict { elements } = &obj.borrow().kind {
|
||||
elements.clone()
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn set_item(
|
||||
}
|
||||
}
|
||||
|
||||
fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
|
||||
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
|
||||
if let PyObjectKind::List { elements } = &obj.borrow().kind {
|
||||
elements.to_vec()
|
||||
} else {
|
||||
|
||||
@@ -98,3 +98,11 @@ pub fn get_item(
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
|
||||
if let PyObjectKind::Tuple { elements } = &obj.borrow().kind {
|
||||
elements.to_vec()
|
||||
} else {
|
||||
panic!("Cannot extract list elements from non-list");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,41 +6,70 @@ use serde::de::Visitor;
|
||||
use serde::ser::{SerializeMap, SerializeSeq};
|
||||
use serde_json;
|
||||
|
||||
use super::super::objtype;
|
||||
use super::super::pyobject::{
|
||||
DictProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use super::super::VirtualMachine;
|
||||
use super::super::{objbool, objdict, objfloat, objint, objlist, objsequence, objstr, objtype};
|
||||
|
||||
impl serde::Serialize for PyObjectKind {
|
||||
// We need to have a VM available to serialise a PyObject based on its subclass, so we implement
|
||||
// PyObject serialisation via a proxy object which holds a reference to a VM
|
||||
struct PyObjectSerializer<'s> {
|
||||
pyobject: &'s PyObjectRef,
|
||||
vm: &'s VirtualMachine,
|
||||
}
|
||||
|
||||
impl<'s> PyObjectSerializer<'s> {
|
||||
fn clone_with_object(&self, pyobject: &'s PyObjectRef) -> PyObjectSerializer {
|
||||
PyObjectSerializer {
|
||||
pyobject,
|
||||
vm: self.vm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> serde::Serialize for PyObjectSerializer<'s> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match self {
|
||||
PyObjectKind::String { value } => serializer.serialize_str(value),
|
||||
PyObjectKind::Integer { value } => serializer.serialize_i32(*value),
|
||||
PyObjectKind::Float { value } => serializer.serialize_f64(*value),
|
||||
PyObjectKind::Boolean { value } => serializer.serialize_bool(*value),
|
||||
PyObjectKind::List { elements } | PyObjectKind::Tuple { elements } => {
|
||||
let serialize_seq_elements =
|
||||
|serializer: S, elements: Vec<PyObjectRef>| -> Result<S::Ok, S::Error> {
|
||||
let mut seq = serializer.serialize_seq(Some(elements.len()))?;
|
||||
for e in elements {
|
||||
match e.borrow().kind {
|
||||
ref kind => seq.serialize_element(kind)?,
|
||||
}
|
||||
seq.serialize_element(&self.clone_with_object(&e))?;
|
||||
}
|
||||
seq.end()
|
||||
};
|
||||
if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.str_type()) {
|
||||
serializer.serialize_str(&objstr::get_value(&self.pyobject))
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.int_type()) {
|
||||
serializer.serialize_i32(objint::get_value(self.pyobject.clone()))
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.float_type()) {
|
||||
serializer.serialize_f64(objfloat::get_value(self.pyobject.clone()))
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.bool_type()) {
|
||||
serializer.serialize_bool(objbool::get_value(self.pyobject))
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.list_type()) {
|
||||
let elements = objlist::get_elements(self.pyobject.clone());
|
||||
serialize_seq_elements(serializer, elements)
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.tuple_type()) {
|
||||
let elements = objsequence::get_elements(self.pyobject.clone());
|
||||
serialize_seq_elements(serializer, elements)
|
||||
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.dict_type()) {
|
||||
let elements = objdict::get_elements(self.pyobject);
|
||||
let mut map = serializer.serialize_map(Some(elements.len()))?;
|
||||
for (key, e) in elements {
|
||||
map.serialize_entry(&key, &self.clone_with_object(&e))?;
|
||||
}
|
||||
PyObjectKind::Dict { elements } => {
|
||||
let mut map = serializer.serialize_map(Some(elements.len()))?;
|
||||
for (key, e) in elements {
|
||||
map.serialize_entry(key, &e.borrow().kind)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
PyObjectKind::None => serializer.serialize_none(),
|
||||
kind => unimplemented!("Object of type '{:?}' is not serializable", kind),
|
||||
map.end()
|
||||
} else if let PyObjectKind::None = self.pyobject.borrow().kind {
|
||||
serializer.serialize_none()
|
||||
} else {
|
||||
unimplemented!(
|
||||
"Object of type '{:?}' is not serializable",
|
||||
self.pyobject.typ()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +201,8 @@ fn dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
// TODO: Implement non-trivial serialisation case
|
||||
arg_check!(vm, args, required = [(obj, None)]);
|
||||
// TODO: Raise an exception for serialisation errors
|
||||
let string = serde_json::to_string(&obj.borrow().kind).unwrap();
|
||||
let serializer = PyObjectSerializer { pyobject: obj, vm };
|
||||
let string = serde_json::to_string(&serializer).unwrap();
|
||||
Ok(vm.context().new_str(string))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user