249 lines
8.4 KiB
Python
249 lines
8.4 KiB
Python
import orjson
|
|
import ujson as json
|
|
|
|
from .listnode import ListNode
|
|
from .nestedinteger import NestedInteger
|
|
from .treenode import TreeNode
|
|
|
|
|
|
class DeserializeError(Exception):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __str__(self):
|
|
return str(self.value)
|
|
|
|
|
|
class __Deserializer__:
|
|
def _deserialize(self, s, t):
|
|
if t[-2:] == "[]":
|
|
return [
|
|
self._deserialize(
|
|
json.dumps(x, escape_forward_slashes=False), t[:-2]
|
|
)
|
|
for x in json.loads(s)
|
|
]
|
|
elif t[-1:] == ">":
|
|
subt = t[5:-1]
|
|
return [
|
|
self._deserialize(
|
|
json.dumps(x, escape_forward_slashes=False), subt
|
|
)
|
|
for x in json.loads(s)
|
|
]
|
|
elif t == "integer":
|
|
return int(s)
|
|
elif t == "long":
|
|
return int(s)
|
|
elif t == "double":
|
|
return float(s)
|
|
elif t == "character":
|
|
return json.loads(s)
|
|
elif t == "boolean":
|
|
return json.loads(s)
|
|
elif t == "string":
|
|
return json.loads(s)
|
|
elif t == "ListNode":
|
|
return ListNode.deserialize(s)
|
|
elif t == "TreeNode":
|
|
return TreeNode.deserialize(s)
|
|
elif t == "NestedInteger":
|
|
return NestedInteger.deserialize(s)
|
|
|
|
# deserialization with validation
|
|
# TODO: we should probably give one of those helper (?) tooltips in the run_code panel, which redirects ppl to a FAQ section # noqa: B950
|
|
# which details what are the allowed values of each type.
|
|
# TODO: write more granular error messages for each input type (do this later after the specification for allowed values is decided) # noqa: B950
|
|
def _deserialize_with_checks(self, s, t): # , validate=False # noqa: C901
|
|
if t[-2:] == "[]":
|
|
try:
|
|
j = json.loads(s)
|
|
assert type(j) == list
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return [
|
|
self._deserialize_with_checks(
|
|
json.dumps(x, escape_forward_slashes=False), t[:-2]
|
|
)
|
|
for x in j
|
|
]
|
|
elif t[-1:] == ">":
|
|
subt = t[5:-1]
|
|
try:
|
|
j = json.loads(s)
|
|
assert type(j) == list
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return [
|
|
self._deserialize_with_checks(
|
|
json.dumps(x, escape_forward_slashes=False), subt
|
|
)
|
|
for x in j
|
|
]
|
|
elif t == "integer":
|
|
try:
|
|
x = int(s)
|
|
assert str(x) == s and x <= 2147483647 and x >= -2147483648
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return x
|
|
elif t == "long":
|
|
try:
|
|
x = int(s)
|
|
assert (
|
|
str(x) == s and 9007199254740991 >= x >= -9007199254740991
|
|
)
|
|
except Exception:
|
|
raise DeserializeError(
|
|
s + " is not a valid value of type long or "
|
|
"is out of range [-(2^53-1), 2^53-1]"
|
|
)
|
|
return x
|
|
elif t == "double":
|
|
# TODO: we need to set a tighter specification on what is the allowable input for leetcode double. # noqa: B950
|
|
# It will probably be a very small subset of the strings which can be cast to float in python. # noqa: B950
|
|
# maybe we will only allow numbers like 4532.345 and -0.432432
|
|
# ^ specification might be similar to this problem https://leetcode.com/problems/valid-number/ # noqa: B950
|
|
try:
|
|
return float(s)
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
elif t == "character":
|
|
# TODO: we also need a tighter specification on what the allowable values of char are for leetcode. # noqa: B950
|
|
# I would strongly prefer to be on the tighter side at first. Eg. only the chars which we have ever used in testcases for existing problems # noqa: B950
|
|
# and no other characters for now.
|
|
# would could dump all such characters into a "permitted.charset"
|
|
try:
|
|
j = json.loads(s)
|
|
c = str(j)
|
|
assert len(c) == 1
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return c
|
|
elif t == "boolean":
|
|
try:
|
|
assert s == "true" or s == "false"
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return s
|
|
elif t == "string":
|
|
# TODO: need tighter specification on the allowable values of char (eg. ascii only) # noqa: B950
|
|
try:
|
|
j = json.loads(s)
|
|
s = str(j)
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
return s
|
|
elif t == "ListNode":
|
|
try:
|
|
return ListNode.deserialize(s)
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
elif t == "TreeNode":
|
|
try:
|
|
return TreeNode.deserialize(s)
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
elif t == "NestedInteger":
|
|
try:
|
|
return NestedInteger.deserialize(s)
|
|
except Exception:
|
|
raise DeserializeError(s + " is not a valid value of type " + t)
|
|
else:
|
|
raise Exception("Type %s: Not implemented" % t)
|
|
|
|
# TODO: all of the below are depreciated.
|
|
# Remove after new serializer/deserializer is deployed
|
|
|
|
def to_integer(self, line):
|
|
return int(line)
|
|
|
|
def to_double(self, line):
|
|
return float(line)
|
|
|
|
def to_char(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_string(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_int_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_double_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_double_2d_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_int_2d_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_char_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_char_2d_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_string_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_string_set(self, line):
|
|
return set(json.loads(line))
|
|
|
|
def to_string_2d_array(self, line):
|
|
return json.loads(line)
|
|
|
|
def to_list_node(self, line):
|
|
return ListNode.deserialize(line)
|
|
|
|
def to_list_node_array(self, line):
|
|
arr2d = json.loads(line)
|
|
lists = []
|
|
for arr in arr2d:
|
|
lists.append(ListNode._array_to_list_node(arr))
|
|
return lists
|
|
|
|
def to_tree_node(self, line):
|
|
return TreeNode.deserialize(line)
|
|
|
|
def to_nested_integer(self, line):
|
|
return NestedInteger.deserialize(line)
|
|
|
|
def to_nested_integer_array(self, line):
|
|
ni = NestedInteger.deserialize(line)
|
|
return ni.getList()
|
|
|
|
|
|
def deserialize_default(obj, type_str):
|
|
if type_str == "ListNode":
|
|
return ListNode._array_to_list_node(obj)
|
|
elif type_str == "TreeNode":
|
|
return TreeNode._array_to_tree_node(obj)
|
|
elif type_str == "NestedInteger":
|
|
return NestedInteger._token_to_nested_integer(obj)
|
|
else:
|
|
return obj
|
|
|
|
|
|
class __DeserializerRapid__:
|
|
def _deserialize_node(self, obj, type_str):
|
|
if type_str[-2:] == "[]":
|
|
return [self._deserialize_node(x, type_str[:-2]) for x in obj]
|
|
elif type_str[-1:] == ">":
|
|
return [self._deserialize_node(x, type_str[5:-1]) for x in obj]
|
|
else:
|
|
return deserialize_default(obj, type_str)
|
|
|
|
def _deserialize(self, obj_str, type_str):
|
|
obj = orjson.loads(obj_str)
|
|
if (
|
|
"ListNode" in type_str
|
|
or "TreeNode" in type_str
|
|
or "NestedInteger" in type_str
|
|
):
|
|
return self._deserialize_node(obj, type_str)
|
|
else:
|
|
return obj
|
|
|