To promote interoperability, safety and ease of integration with web applications, the default content type for all properties, actions and events is the JSON data format - application/json.
The default included implementation relies on C++ (msgspec), therefore one need not worry about the performance for a large number of use cases, including lists of 1000 to 10000 floats
and large nested dictionaries or objects.
Changing the Default Serializer
One would use the Serializers singleton to set the desired serialization on the specific property, action or event to overcome the default setting on an individual basis:
Change Serializer for Specific Properties, Actions or Events
fromhololinked.serializersimportSerializers# Using a pickle serializer for a list propertySerializers.register_for_object(OceanOpticsSpectrometer.spectrum,Serializers.pickle)# pickle is not recommended, use message pack if possible# from hololinked.config import global_config# global_config.ALLOW_PICKLE = True # default False# Using message pack for an action or eventSerializers.register_for_object(objekt=OceanOpticsSpectrometer.update_background_intensity,serializer=Serializers.msgpack)Serializers.register_for_object(objekt=OceanOpticsSpectrometer.intensity_measurement_event,serializer=Serializers.msgpack)OceanOpticsSpectrometer(id="spectro1").run_with_http_server()
i.e., other properties, actions or events will still use the default (JSON) serialization. By referring the property, action or event at the class level, the data format change will be reflected for all instances. To overload the content type per Thing instance, specify the thing_id as well:
fromhololinked.serializersimportSerializersspectrometer=OceanOpticsSpectrometer(id='spectro1')# all instances of OceanOpticsSpectrometer class will use msgpack for spectrum propertySerializers.register_for_object(objekt=OceanOpticsSpectrometer.spectrum,serializer=Serializers.msgpack)# This specific instance will use pickle for spectrum property, # other instances will use msgpackSerializers.register_for_object_per_thing_instance(thing_id=spectrometer.id,objekt=OceanOpticsSpectrometer.spectrum.name,# accepts only string nameserializer=Serializers.pickle)spectrometer.run(...)
To overload the default serializer for the entire Thinginstance altogether:
fromhololinked.serializersimportSerializersspectrometer=OceanOpticsSpectrometer(id='spectro1')# instance will use msgpack for all properties, actions and eventsSerializers.register_for_thing_instance(thing_id=spectrometer.id,serializer=Serializers.msgpack)# specific property will use pickle, other properties, actions # and events will use msgpackSerializers.register_for_object_per_thing_instance(thing_id=spectrometer.id,objekt=OceanOpticsSpectrometer.spectrum.name,# accepts only string nameserializer=Serializers.pickle)spectrometer.run(...)
The order of priority is as follows (from highest to lowest):
Per-Thing-instance overloads for specific properties, actions or events.
Per-Thing-instance overloads for the entire Thing instance.
Per-Thing-class overloads for specific properties, actions or events.
Fallback to the default serializer. It is possible to change the default serializer, see API reference.
The singleton behaviour of Serializers ensures that all registrations are available across all protocol servers within the same process.
Built-in Serializers
The following serializers are supported out of the box:
Serializers.json: Default JSON serializer using msgspec library
Serializers.msgpack: MessagePack serializer using msgspec library
Serializers.pickle: Python's built-in pickle serializer (not recommended for untrusted clients or servers)
Serializers.text: Plain text serializer for string data
Custom Serializers
One can create custom serializers by subclassing the BaseSerializer and implementing dumps, loads, and content_type methods.
Then, register the custom serializer for a specific property, action or event using the Serializers singleton as shown above.
fromhololinked.serializersimportBaseSerializer,SerializersimportimageioimportioimportnumpyclassPNGImageSerializer(BaseSerializer):@classmethoddefdumps(cls,image:numpy.ndarray)->bytes:# Implement serialization logic herebuffer=io.BytesIO()imageio.imwrite(buffer,image,format='png')returnbuffer.getvalue()@classmethoddefloads(cls,png:bytes)->numpy.ndarray:# Implement deserialization logic herebuffer=io.BytesIO(png)image=imageio.imread(buffer,format='png')returnimage@propertydefcontent_type(self)->str:return"image/png"# register the serializer firstSerializers.register(PNGImageSerializer)# overload the serializer for a specific property/action/eventSerializers.register_for_object(objekt=IDSCamera.image,serializer=Serializers.PNGImageSerializer# use the same name for the serializer as the class name,# or a name provided in the register method)IDSCamera(id='nearfield').run_with_http_server()
All three methods (dumps, loads, and content_type) must be implemented for the custom serializer to work correctly,
otherwise an error will be raised at runtime.