Writing a serializer or a deserializer – Effective Volto – Backend

Writing a serializer or a deserializer

Writing a serializer or a deserializer#

In plone.restapi we have a hierarchy, several levels deep, of serializer and deserializer adapters. Keep in mind that "serializing" is the operation where we transform Python objects to a representation (JSON) and deserializing is when we take that representation (JSON coming from the browser POST, for example) and convert it to live Python objects. The (de)serializers, with the type-based lookups are a great example of ZCML use, as they're all implemented as adapters for the content+request => ISomeSerializationInferface.

Here's that logic chain of that hierarchy:

  1. content based, where the class of the context item is used as discriminator in the adaptor to plone.restapi.interfaces.ISerializeToJson (and the counterpart IDeserializeToJson. See the DX Content serializer.

  2. field based, used when processing DX Content, where each field/property is adapted for endpoint serialization with the IFieldSerializer / IFieldDeserializer. See the DX Field serializers.

  3. block based, where we take the JSON data bits that represent a Volto block data and transform it (see the Writing a block transform chapter).

  4. block value based, where each Python basic data value needs to be transformed into a JSON-compatible representation, with the IJsonCompatible adaptor (use json_compatible() helper for this. See the converters.py module with these basic serializers.

Here's how the Folder serializer looks like:

@implementer(ISerializeToJson)
@adapter(IDexterityContainer, Interface)
class SerializeFolderToJson(SerializeToJson):
    def _build_query(self):
        path = "/".join(self.context.getPhysicalPath())
        query = {
            "path": {"depth": 1, "query": path},
            "sort_on": "getObjPositionInParent",
        }
        return query

    def __call__(self, version=None, include_items=True):
        folder_metadata = super().__call__(version=version)

        folder_metadata.update({"is_folderish": True})
        result = folder_metadata

        include_items = self.request.form.get("include_items", include_items)
        include_items = boolean_value(include_items)
        if include_items:
            query = self._build_query()

            catalog = getToolByName(self.context, "portal_catalog")
            brains = catalog(query)

            batch = HypermediaBatch(self.request, brains)

            result["items_total"] = batch.items_total
            if batch.links:
                result["batching"] = batch.links

            if "fullobjects" in list(self.request.form):
                result["items"] = getMultiAdapter(
                    (brains, self.request), ISerializeToJson
                )(fullobjects=True)["items"]
            else:
                result["items"] = [
                    getMultiAdapter((brain, self.request), ISerializeToJsonSummary)()
                    for brain in batch
                ]
        return result