DEV Community

Maik Derstappen
Maik Derstappen

Posted on • Originally published at mrtango.planetcrazy.de on

Writing a custom serializer for Plone REST API

The following code shows two custom FieldSerializer adapters which handle the values of Choice and MultiChoice (Collection) fields. For doing this, we register one adapter for the IChoice interface and one adapter for ICollection interface. Both also support vocabularies provided by collective.taxonomy.

By default the Plone REST API would give us just the plain tokens back, because that's what is actually stored when you use a Selection or Multiselection field. But on the client side we want the token and also the title, so that we can easily show it. Therefor we want something like this as the value for a Choice field.

Expected response

IChoice field

"solution\_category": {
    "title": "A nice title",
    "token": "a-nice-title"
},

Enter fullscreen mode Exit fullscreen mode

ICollection field

"solution\_category": [
    {
        "title": "A nice title",
        "token": "a-nice-title"
    },
    {
        "title": "An awesome title",
        "token": "an-awesome-title"
    },
]

Enter fullscreen mode Exit fullscreen mode

Implementation

restapi.py

from plone.dexterity.interfaces import IDexterityContent
from plone.restapi.interfaces import IFieldSerializer
from plone.restapi.serializer.converters import json\_compatible
from zope.component import adapter
from zope.component import getUtility
from zope.interface import implementer
from zope.interface import Interface
from zope.schema.interfaces import IChoice
from zope.schema.interfaces import ICollection
from zope.schema.interfaces import IVocabularyFactory

def \_get\_vocab\_term(context, field, value):
    """ Get vocab term dict
 returns: {'token': token, 'title': title}
 """
    vocab\_term = {
        'token': None,
        'title': None,
    }
    vocab\_term['token'] = value
    factory = getUtility(IVocabularyFactory, field)
    if not factory:
        return vocab\_term

    # collective.taxonomy support:
    if hasattr(factory, 'translate'):
        vocab\_term['title'] = \_get\_taxonomy\_vocab\_title(
            context,
            factory,
            value,
        )
    elif IVocabularyFactory.providedBy(factory):
        vocab\_term['title'] = \_get\_vocab\_title(
            context,
            factory,
            value,
        )
    return vocab\_term

def \_get\_taxonomy\_vocab\_title(context, factory, value):
        vocab\_title = factory.translate(
            value,
            context=context,
        )
        return vocab\_title

def \_get\_vocab\_title(context, factory, value):
    vocab = factory(context)
    vocab\_title = vocab.getTermByToken(value).title
    return vocab\_title

class BaseFieldSerializer(object):

    def \_\_init\_\_(self, field, context, request):
        self.context = context
        self.request = request
        self.field = field

    def \_\_call\_\_(self):
        return json\_compatible(self.get\_value())

@adapter(IChoice, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class ChoiceFieldSerializer(BaseFieldSerializer):
    """
 """
    def get\_value(self, default=None):
        value = getattr(
            self.field.interface(self.context),
            self.field.\_\_name\_\_,
            default,
        )
        if not value:
            return

        term = \_get\_vocab\_term(
            self.context,
            self.field.vocabularyName,
            value,
        )
        return term

@adapter(ICollection, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class CollectionFieldSerializer(BaseFieldSerializer):
    """
 """
    def get\_value(self, default=None):
        terms = []
        values = getattr(
            self.field.interface(self.context),
            self.field.\_\_name\_\_,
            default,
        )
        if not values:
            return
        if not IChoice.providedBy(self.field.value\_type):
            return

        for value in values:
            term = \_get\_vocab\_term(
                self.context,
                self.field.value\_type.vocabularyName,
                value,
            )
            terms.append(term)
        return terms

Enter fullscreen mode Exit fullscreen mode

Registration

To activate this, we need to register the two adapters:

configure.zcml

<adapter factory=".restapi.ChoiceFieldSerializer" />
<adapter factory=".restapi.CollectionFieldSerializer" />

Enter fullscreen mode Exit fullscreen mode

Top comments (0)