Writing a serializer or a deserializer#

In plone.restapi we have an hierarchy, several levels deep, of serializers and deserializers 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.

  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. 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