Skip to content
This repository was archived by the owner on Jan 8, 2024. It is now read-only.

Commit 4391225

Browse files
committed
Added Base64 Serialization for very large arrays (Image-Like)
1 parent 44ddb66 commit 4391225

4 files changed

Lines changed: 107 additions & 19 deletions

File tree

JsonToObject/reverseEntityAttribute.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
__version__ = "0.0.1a"
1919
__status__ = "Developement"
2020

21+
import base64
22+
import array
23+
try:
24+
import urllib.parse as quote
25+
except ImportError:
26+
import urllib as quote
27+
28+
2129
### Error Messages
2230
TYPE_VALUE_METADATA_NOT_DEFINED_MESSAGE = "One of the following is not defined in json: {type|value}"
2331
VALUE_EMPTY_MESSAGE = "The Value entered cannot be empty!"
@@ -103,6 +111,29 @@ def __init__(self, _dict, useMetaData=True):
103111
rea = ReverseEntityAttribute(value, useMetaData)
104112
self.value[key] = rea.getValue()
105113

114+
elif _dict['type'].lower() == "base64":
115+
# Case we have a base64 String:
116+
# First Unquote Special Characters
117+
tempValue = quote.unquote(_dict['value'])
118+
119+
# Decode Base64 String into Bytes
120+
tempValue = base64.b64decode(tempValue)
121+
122+
# Retrieve Information about int8 or uint8
123+
if "metadata" in _dict and "dataType" in _dict["metadata"]:
124+
datatype = _dict['metadata']['dataType']['value']
125+
else:
126+
raise ValueError("Unknown Object-Type: " + _dict['type'] + ". The MetaData does not specify what the actual DataType is.")
127+
128+
# convert back to integers
129+
if datatype == "int8[]":
130+
tempValue = array.array("b", tempValue)
131+
else:
132+
tempValue = array.array("B", tempValue)
133+
134+
# Change DataType to primitive python list
135+
self.value = tempValue.tolist()
136+
106137
else:
107138
# Maybe a class with key, value or another JSON object, check if you can iterate!
108139
if (not hasattr(_dict['value'], 'items')):

ObjectToJson/entity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def setObject(self, _object, dataTypeDict, ignorePythonMetaData, showIdValue=Tru
7474
# Object contains invalid key-name, ignore!
7575
pass
7676
else:
77-
self.__dict__[key] = EntityAttribute(value, ignorePythonMetaData, dataTypeDict.get(key))
77+
self.__dict__[key] = EntityAttribute(value, ignorePythonMetaData, dataTypeDict.get(key), baseEntity=True)
7878
except AttributeError as ex:
7979
raise ValueError(ERROR_MESSAGE_ATTTRIBUTE, ex)
8080

ObjectToJson/entityAttribute.py

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@
1919
__status__ = "Developement"
2020

2121
import sys
22+
import array
23+
import base64
2224

25+
try:
26+
import urllib.parse as quote
27+
except ImportError:
28+
import urllib as quote
2329

2430
class EntityAttribute():
2531
""" Here the actual Conversion to the correct JSON-Format happens
@@ -30,59 +36,72 @@ class EntityAttribute():
3036
"""
3137
python_version = sys.version_info
3238

33-
def __init__(self, _object, ipmd, concreteDataType=None):
39+
def __init__(self, _object, ipmd, concreteDataType=None, baseEntity=False):
3440
self.value = _object
3541
self.type = ""
3642
self.metadata = dict()
43+
if baseEntity:
44+
self.setConcreteMetaData(concreteDataType)
3745
objectType = type(_object)
3846

39-
4047
# Simply if-then-else to the Json fromat
4148
if(objectType is type(None)):
4249
pass
4350
elif objectType is bool:
4451
self.type = "boolean"
4552
self.value = bool(_object)
53+
# self.setConcreteMetaData(concreteDataType)
4654
elif objectType is int:
4755
self.type = "number"
4856
self.value = int(_object)
4957
self.setPythonMetaData(ipmd, "int")
58+
# self.setConcreteMetaData(concreteDataType)
5059
elif objectType is float:
5160
self.type = "number"
5261
self.value = float(_object)
5362
self.setPythonMetaData(ipmd, "float")
63+
# self.setConcreteMetaData(concreteDataType)
5464
elif self.python_version < (3,0) and objectType is long: # Check explicitly if Python 2 is used
5565
self.type = "number"
5666
self.value = long(_object)
5767
self.setPythonMetaData(ipmd, "long")
68+
# self.setConcreteMetaData(concreteDataType)
5869
elif objectType is complex:
5970
self.type = "array"
6071
t = complex(_object)
6172
self.value = [EntityAttribute(t.real, ipmd), EntityAttribute(t.imag, ipmd)]
6273
self.setPythonMetaData(ipmd, "complex")
74+
# self.setConcreteMetaData(concreteDataType)
6375
elif objectType is str:
6476
self.type = "string"
6577
self.value = str(_object)
78+
# self.setConcreteMetaData(concreteDataType)
6679
elif self.python_version < (3,0) and objectType is unicode: # Check explicitly if Python 2 is used
6780
self.type = "string"
6881
self.value = unicode(_object)
82+
# self.setConcreteMetaData(concreteDataType)
6983
self.setPythonMetaData(ipmd, "unicode")
7084
elif objectType is tuple:
7185
self.type = "array"
7286
self.value = []
7387
self.setPythonMetaData(ipmd, "tuple")
88+
self.setConcreteMetaData(concreteDataType)
7489
for item in _object:
7590
self.value.append(EntityAttribute(item, ipmd))
7691
elif objectType is list:
7792
self.type = "array"
7893
self.value = []
94+
self.setConcreteMetaData(concreteDataType)
7995
for item in _object:
8096
self.value.append(EntityAttribute(item, ipmd))
8197
elif objectType is dict:
8298
self.type = "object"
8399
tempDict = {}
84100
for key, value in _object.items():
85-
tempDict[key] = EntityAttribute(value,ipmd )
101+
innerConcreteMetaData = None
102+
if concreteDataType is not None and key in concreteDataType:
103+
innerConcreteMetaData = concreteDataType[key]
104+
tempDict[key] = EntityAttribute(value, ipmd, innerConcreteMetaData)
86105
self.value = tempDict
87106
else:
88107
# Case it is a Class
@@ -94,22 +113,54 @@ def __init__(self, _object, ipmd, concreteDataType=None):
94113
else:
95114
raise ValueError("Cannot get attrs from {}".format(str(_object)))
96115

97-
if hasattr(_object, '_type'): # ROS-Specific Type-Declaration
116+
if hasattr(_object, '_type') and hasattr(_object, '_slot_types') and hasattr(_object, '__slots__'): # ROS-Specific Type-Declaration
117+
### This is a special Class from ROS!
98118
self.type = _object._type.replace("/", ".") # Needs to be replaced Fiware does not allow a '/'
99-
else:
100-
self.type = _object.__class__.__name__
101-
self.setPythonMetaData(ipmd, "class")
102-
tempDict = {}
103-
for key in iterL:
104-
if key.startswith('_'):
105-
continue
106-
tempDict[key] = EntityAttribute(getattr(_object, key), ipmd)
107-
self.value = tempDict
119+
self.setPythonMetaData(ipmd, "class")
120+
# Special Case 'Image-like'-Data in ROS (very long 'int8[]'- and 'uint8[]' - arrays)
121+
# These are converted into Base64 (escaped)
122+
tempDict = {}
123+
for key, key_type in zip(_object.__slots__, _object._slot_types):
124+
if key.startswith('_'):
125+
continue
126+
if (key_type == 'int8[]' or key_type == 'uint8[]') and len(getattr(_object, key)) >= 256:
127+
# TODO DL 256 -> Threshold?
128+
# Generate Base64 String of the Array:
129+
tempDict[key] = EntityAttribute(None, ipmd)
130+
tempDict[key].type = "base64"
108131

132+
# Either generate unsigned or signed byte-array
133+
if key_type == 'int8[]':
134+
tempDict[key].value = array.array('b', getattr(_object, key)).tostring()
135+
else:
136+
tempDict[key].value = array.array('B', getattr(_object, key)).tostring()
137+
138+
# Form that Byte-Array: generate Base64 String
139+
tempDict[key].value = base64.b64encode(tempDict[key].value)
109140

110-
if concreteDataType is not None:
111-
self.metadata["dataType"] = dict(type="dataType", value=concreteDataType)
112-
pass
141+
# Escape Special Characters:
142+
tempDict[key].value = quote.quote(tempDict[key].value)
143+
tempDict[key].metadata = dict()
144+
self.setConcreteMetaData(concreteDataType[key], tempDict[key])
145+
else:
146+
innerConcreteMetaData = None
147+
if concreteDataType is not None and key in concreteDataType:
148+
innerConcreteMetaData = concreteDataType[key]
149+
tempDict[key] = EntityAttribute(getattr(_object, key), ipmd, innerConcreteMetaData)
150+
self.value = tempDict
151+
else:
152+
# Simple Class. Recursively retrieve the other values
153+
self.type = _object.__class__.__name__
154+
self.setPythonMetaData(ipmd, "class")
155+
tempDict = {}
156+
for key in iterL:
157+
if key.startswith('_'):
158+
continue
159+
innerConcreteMetaData = None
160+
if concreteDataType is not None and key in concreteDataType:
161+
innerConcreteMetaData = concreteDataType[key]
162+
tempDict[key] = EntityAttribute(getattr(_object, key), ipmd, innerConcreteMetaData)
163+
self.value = tempDict
113164

114165
# Remove metadata-Attribute if it is empty (minimizing the JSON)
115166
if self.metadata == {} :
@@ -118,4 +169,9 @@ def __init__(self, _object, ipmd, concreteDataType=None):
118169
def setPythonMetaData(self, ignorePythonMetaData, val):
119170
if not ignorePythonMetaData:
120171
self.metadata["python"] = dict(type="dataType", value=val)
121-
172+
173+
def setConcreteMetaData(self, val, obj= None):
174+
if val is not None and obj is None:
175+
self.metadata["dataType"] = dict(type="dataType", value=val)
176+
elif val is not None:
177+
obj.metadata["dataType"] = dict(type="dataType", value=val)

ObjectToJson/testEntityAttribute.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def test_EntityAttributeComplex_ignoreMetaData_True(self):
140140
self.assertEqual(ea.type, "array")
141141

142142
def test_EntityAttributeFloat32List_concreteDataType(self):
143-
ea = EA(ClassInt(), True, concreteDataType=dict(int="uint32_t"))
143+
ea = EA(ClassInt(), True, concreteDataType=dict(int="uint32_t"), baseEntity=True)
144144
self.assertTrue(hasattr(ea, 'metadata'))
145145
self.assertEqual(ea.metadata, dict(
146146
dataType=dict(type="dataType", value=dict(int='uint32_t'))))
@@ -196,6 +196,7 @@ def __init__(self):
196196

197197
class RosClassWithSlotsInt(object):
198198
__slots__ = ['val1', '_type']
199+
_slot_types = ['uint8', 'string']
199200
def __init__(self):
200201
self.val1 = 1
201202
self._type = "RosClass/Integer" # Example-Type

0 commit comments

Comments
 (0)