{"action":"created","comment":{"url":"https://api.github.com/repos/plone/plone.restapi/pulls/comments/44900231","id":44900231,"diff_hunk":"@@ -0,0 +1,68 @@\n+\n+=====================\n+ Customizing the API\n+=====================\n+\n+Content serialization\n+=====================\n+\n+Dexterity fields\n+----------------\n+\n+The API automatically converts all field values to JSON compatible\n+data, whenever possible.\n+However, you might use fields which store data which cannot automatically\n+be converted or you might want to customize the representation of certain\n+fields.\n+\n+For extending or changing the serializing of certain dexterity fields you\n+need to register an ``IFieldSerializer``-adapter.\n+\n+Example:\n+\n+.. code:: python\n+\n+    from plone.customfield.interfaces import ICustomField\n+    from plone.dexterity.interfaces import IDexterityContent\n+    from plone.restapi.interfaces import IFieldSerializer\n+    from plone.restapi.serializer.converters import json_compatible\n+    from plone.restapi.serializer.dxfields import DefaultFieldSerializer\n+    from zope.component import adapter\n+    from zope.interface import Interface\n+    from zope.interface import implementer\n+\n+\n+    @adapter(ICustomField, IDexterityContent, Interface)\n+    @implementer(IFieldSerializer)\n+    class CustomFieldSerializer(DefaultFieldSerializer):\n+\n+        def __call__(self):\n+            value = self.get_value()\n+            if value is not None:\n+                value = value.output()","path":"docs/source/customization.rst","position":42,"original_position":42,"commit_id":"7ede747cea15a40e155d768a4931c4f2db3e0e44","original_commit_id":"cd3a329ac15d46aafea2edf84fd01644d682a514","user":{"login":"tisto","id":632105,"avatar_url":"https://avatars.githubusercontent.com/u/632105?v=3","gravatar_id":"","url":"https://api.github.com/users/tisto","html_url":"https://github.com/tisto","followers_url":"https://api.github.com/users/tisto/followers","following_url":"https://api.github.com/users/tisto/following{/other_user}","gists_url":"https://api.github.com/users/tisto/gists{/gist_id}","starred_url":"https://api.github.com/users/tisto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tisto/subscriptions","organizations_url":"https://api.github.com/users/tisto/orgs","repos_url":"https://api.github.com/users/tisto/repos","events_url":"https://api.github.com/users/tisto/events{/privacy}","received_events_url":"https://api.github.com/users/tisto/received_events","type":"User","site_admin":false},"body":"+1","created_at":"2015-11-16T09:00:43Z","updated_at":"2015-11-16T09:00:43Z","html_url":"https://github.com/plone/plone.restapi/pull/39#discussion_r44900231","pull_request_url":"https://api.github.com/repos/plone/plone.restapi/pulls/39","_links":{"self":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/comments/44900231"},"html":{"href":"https://github.com/plone/plone.restapi/pull/39#discussion_r44900231"},"pull_request":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/39"}}},"pull_request":{"url":"https://api.github.com/repos/plone/plone.restapi/pulls/39","id":49934969,"html_url":"https://github.com/plone/plone.restapi/pull/39","diff_url":"https://github.com/plone/plone.restapi/pull/39.diff","patch_url":"https://github.com/plone/plone.restapi/pull/39.patch","issue_url":"https://api.github.com/repos/plone/plone.restapi/issues/39","number":39,"state":"open","locked":false,"title":"Field serialization adapters","user":{"login":"jone","id":7469,"avatar_url":"https://avatars.githubusercontent.com/u/7469?v=3","gravatar_id":"","url":"https://api.github.com/users/jone","html_url":"https://github.com/jone","followers_url":"https://api.github.com/users/jone/followers","following_url":"https://api.github.com/users/jone/following{/other_user}","gists_url":"https://api.github.com/users/jone/gists{/gist_id}","starred_url":"https://api.github.com/users/jone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jone/subscriptions","organizations_url":"https://api.github.com/users/jone/orgs","repos_url":"https://api.github.com/users/jone/repos","events_url":"https://api.github.com/users/jone/events{/privacy}","received_events_url":"https://api.github.com/users/jone/received_events","type":"User","site_admin":false},"body":"Closes #38 \r\n\r\nAs discussed in #38 this PR introduces field serializer adapters for dexterity based content types in order to make the serialization better customizable and extendable and provide a dependency injection entry point.\r\n\r\nCloses #38, #42, #4\r\n\r\n#### ``IFieldSerializer``\r\nThe field serializer adapter adapts the dexterity field, the context and the request. It's duty is to convert the value of the adapted field on the current context to a data structure which is supported by the JSON module.\r\nThis provides the entry point for customizing how a certain field or a type of fields should be represented in the JSON.\r\n\r\n#### ``IJsonCompatible``\r\nThe json compatibel adapter converts values. It does not know anything about fields and has no context. The idea here is that is possible to recursively convert data. This is especially important for supporting list fields, object fields, data grid fields etc.\r\n\r\n#### Discussion\r\n\r\nI tried to make as few changes as possible but do a fully working implementation. I'd like to continue with supporting relations (#43), using the ``IJsonCompatible`` converters for Plone site representation too, and more.\r\n\r\nHere some notes about this PR:\r\n- I switch to using dexterity's `iterSchemata` and removed our custom code. See #42, #4 \r\n  - non-field values, such as ``meta_type`` are no longer included\r\n  - some fields such as ``effective`` did not use the field value but direct attribute access, which had a different result, which is fixed now\r\n- I have moved image scaling mechanism to a field serializer and moved everything into a dict, acting as a value for this field. This seems more consistent to me.\r\n- I could eliminate the custom field- and image-content-converters (``ISerializeToJson``) and use the general dexterity converter as logic was moved to field serializers.\r\n- Richtext-Values are represented with only the output. We might change that with frames but I think that deserves a separate issue / PR. Discussion needed.","created_at":"2015-11-06T10:00:48Z","updated_at":"2015-11-16T09:00:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"689c258e85615afbe2b669d558ca8200d57261a2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/plone/plone.restapi/pulls/39/commits","review_comments_url":"https://api.github.com/repos/plone/plone.restapi/pulls/39/comments","review_comment_url":"https://api.github.com/repos/plone/plone.restapi/pulls/comments{/number}","comments_url":"https://api.github.com/repos/plone/plone.restapi/issues/39/comments","statuses_url":"https://api.github.com/repos/plone/plone.restapi/statuses/7ede747cea15a40e155d768a4931c4f2db3e0e44","head":{"label":"plone:jone-field-serializers","ref":"jone-field-serializers","sha":"7ede747cea15a40e155d768a4931c4f2db3e0e44","user":{"login":"plone","id":72034,"avatar_url":"https://avatars.githubusercontent.com/u/72034?v=3","gravatar_id":"","url":"https://api.github.com/users/plone","html_url":"https://github.com/plone","followers_url":"https://api.github.com/users/plone/followers","following_url":"https://api.github.com/users/plone/following{/other_user}","gists_url":"https://api.github.com/users/plone/gists{/gist_id}","starred_url":"https://api.github.com/users/plone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plone/subscriptions","organizations_url":"https://api.github.com/users/plone/orgs","repos_url":"https://api.github.com/users/plone/repos","events_url":"https://api.github.com/users/plone/events{/privacy}","received_events_url":"https://api.github.com/users/plone/received_events","type":"Organization","site_admin":false},"repo":{"id":19173345,"name":"plone.restapi","full_name":"plone/plone.restapi","owner":{"login":"plone","id":72034,"avatar_url":"https://avatars.githubusercontent.com/u/72034?v=3","gravatar_id":"","url":"https://api.github.com/users/plone","html_url":"https://github.com/plone","followers_url":"https://api.github.com/users/plone/followers","following_url":"https://api.github.com/users/plone/following{/other_user}","gists_url":"https://api.github.com/users/plone/gists{/gist_id}","starred_url":"https://api.github.com/users/plone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plone/subscriptions","organizations_url":"https://api.github.com/users/plone/orgs","repos_url":"https://api.github.com/users/plone/repos","events_url":"https://api.github.com/users/plone/events{/privacy}","received_events_url":"https://api.github.com/users/plone/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/plone/plone.restapi","description":"RESTful hypermedia API for Plone.","fork":false,"url":"https://api.github.com/repos/plone/plone.restapi","forks_url":"https://api.github.com/repos/plone/plone.restapi/forks","keys_url":"https://api.github.com/repos/plone/plone.restapi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/plone/plone.restapi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/plone/plone.restapi/teams","hooks_url":"https://api.github.com/repos/plone/plone.restapi/hooks","issue_events_url":"https://api.github.com/repos/plone/plone.restapi/issues/events{/number}","events_url":"https://api.github.com/repos/plone/plone.restapi/events","assignees_url":"https://api.github.com/repos/plone/plone.restapi/assignees{/user}","branches_url":"https://api.github.com/repos/plone/plone.restapi/branches{/branch}","tags_url":"https://api.github.com/repos/plone/plone.restapi/tags","blobs_url":"https://api.github.com/repos/plone/plone.restapi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/plone/plone.restapi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/plone/plone.restapi/git/refs{/sha}","trees_url":"https://api.github.com/repos/plone/plone.restapi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/plone/plone.restapi/statuses/{sha}","languages_url":"https://api.github.com/repos/plone/plone.restapi/languages","stargazers_url":"https://api.github.com/repos/plone/plone.restapi/stargazers","contributors_url":"https://api.github.com/repos/plone/plone.restapi/contributors","subscribers_url":"https://api.github.com/repos/plone/plone.restapi/subscribers","subscription_url":"https://api.github.com/repos/plone/plone.restapi/subscription","commits_url":"https://api.github.com/repos/plone/plone.restapi/commits{/sha}","git_commits_url":"https://api.github.com/repos/plone/plone.restapi/git/commits{/sha}","comments_url":"https://api.github.com/repos/plone/plone.restapi/comments{/number}","issue_comment_url":"https://api.github.com/repos/plone/plone.restapi/issues/comments{/number}","contents_url":"https://api.github.com/repos/plone/plone.restapi/contents/{+path}","compare_url":"https://api.github.com/repos/plone/plone.restapi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/plone/plone.restapi/merges","archive_url":"https://api.github.com/repos/plone/plone.restapi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/plone/plone.restapi/downloads","issues_url":"https://api.github.com/repos/plone/plone.restapi/issues{/number}","pulls_url":"https://api.github.com/repos/plone/plone.restapi/pulls{/number}","milestones_url":"https://api.github.com/repos/plone/plone.restapi/milestones{/number}","notifications_url":"https://api.github.com/repos/plone/plone.restapi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/plone/plone.restapi/labels{/name}","releases_url":"https://api.github.com/repos/plone/plone.restapi/releases{/id}","created_at":"2014-04-26T08:59:40Z","updated_at":"2015-10-27T19:32:33Z","pushed_at":"2015-11-13T12:45:25Z","git_url":"git://github.com/plone/plone.restapi.git","ssh_url":"git@github.com:plone/plone.restapi.git","clone_url":"https://github.com/plone/plone.restapi.git","svn_url":"https://github.com/plone/plone.restapi","homepage":"","size":1003,"stargazers_count":20,"watchers_count":20,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":27,"forks":4,"open_issues":27,"watchers":20,"default_branch":"master"}},"base":{"label":"plone:master","ref":"master","sha":"e824b9479b5e74dfb362146dd027168ff17f8a9b","user":{"login":"plone","id":72034,"avatar_url":"https://avatars.githubusercontent.com/u/72034?v=3","gravatar_id":"","url":"https://api.github.com/users/plone","html_url":"https://github.com/plone","followers_url":"https://api.github.com/users/plone/followers","following_url":"https://api.github.com/users/plone/following{/other_user}","gists_url":"https://api.github.com/users/plone/gists{/gist_id}","starred_url":"https://api.github.com/users/plone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plone/subscriptions","organizations_url":"https://api.github.com/users/plone/orgs","repos_url":"https://api.github.com/users/plone/repos","events_url":"https://api.github.com/users/plone/events{/privacy}","received_events_url":"https://api.github.com/users/plone/received_events","type":"Organization","site_admin":false},"repo":{"id":19173345,"name":"plone.restapi","full_name":"plone/plone.restapi","owner":{"login":"plone","id":72034,"avatar_url":"https://avatars.githubusercontent.com/u/72034?v=3","gravatar_id":"","url":"https://api.github.com/users/plone","html_url":"https://github.com/plone","followers_url":"https://api.github.com/users/plone/followers","following_url":"https://api.github.com/users/plone/following{/other_user}","gists_url":"https://api.github.com/users/plone/gists{/gist_id}","starred_url":"https://api.github.com/users/plone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/plone/subscriptions","organizations_url":"https://api.github.com/users/plone/orgs","repos_url":"https://api.github.com/users/plone/repos","events_url":"https://api.github.com/users/plone/events{/privacy}","received_events_url":"https://api.github.com/users/plone/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/plone/plone.restapi","description":"RESTful hypermedia API for Plone.","fork":false,"url":"https://api.github.com/repos/plone/plone.restapi","forks_url":"https://api.github.com/repos/plone/plone.restapi/forks","keys_url":"https://api.github.com/repos/plone/plone.restapi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/plone/plone.restapi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/plone/plone.restapi/teams","hooks_url":"https://api.github.com/repos/plone/plone.restapi/hooks","issue_events_url":"https://api.github.com/repos/plone/plone.restapi/issues/events{/number}","events_url":"https://api.github.com/repos/plone/plone.restapi/events","assignees_url":"https://api.github.com/repos/plone/plone.restapi/assignees{/user}","branches_url":"https://api.github.com/repos/plone/plone.restapi/branches{/branch}","tags_url":"https://api.github.com/repos/plone/plone.restapi/tags","blobs_url":"https://api.github.com/repos/plone/plone.restapi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/plone/plone.restapi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/plone/plone.restapi/git/refs{/sha}","trees_url":"https://api.github.com/repos/plone/plone.restapi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/plone/plone.restapi/statuses/{sha}","languages_url":"https://api.github.com/repos/plone/plone.restapi/languages","stargazers_url":"https://api.github.com/repos/plone/plone.restapi/stargazers","contributors_url":"https://api.github.com/repos/plone/plone.restapi/contributors","subscribers_url":"https://api.github.com/repos/plone/plone.restapi/subscribers","subscription_url":"https://api.github.com/repos/plone/plone.restapi/subscription","commits_url":"https://api.github.com/repos/plone/plone.restapi/commits{/sha}","git_commits_url":"https://api.github.com/repos/plone/plone.restapi/git/commits{/sha}","comments_url":"https://api.github.com/repos/plone/plone.restapi/comments{/number}","issue_comment_url":"https://api.github.com/repos/plone/plone.restapi/issues/comments{/number}","contents_url":"https://api.github.com/repos/plone/plone.restapi/contents/{+path}","compare_url":"https://api.github.com/repos/plone/plone.restapi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/plone/plone.restapi/merges","archive_url":"https://api.github.com/repos/plone/plone.restapi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/plone/plone.restapi/downloads","issues_url":"https://api.github.com/repos/plone/plone.restapi/issues{/number}","pulls_url":"https://api.github.com/repos/plone/plone.restapi/pulls{/number}","milestones_url":"https://api.github.com/repos/plone/plone.restapi/milestones{/number}","notifications_url":"https://api.github.com/repos/plone/plone.restapi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/plone/plone.restapi/labels{/name}","releases_url":"https://api.github.com/repos/plone/plone.restapi/releases{/id}","created_at":"2014-04-26T08:59:40Z","updated_at":"2015-10-27T19:32:33Z","pushed_at":"2015-11-13T12:45:25Z","git_url":"git://github.com/plone/plone.restapi.git","ssh_url":"git@github.com:plone/plone.restapi.git","clone_url":"https://github.com/plone/plone.restapi.git","svn_url":"https://github.com/plone/plone.restapi","homepage":"","size":1003,"stargazers_count":20,"watchers_count":20,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":27,"forks":4,"open_issues":27,"watchers":20,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/39"},"html":{"href":"https://github.com/plone/plone.restapi/pull/39"},"issue":{"href":"https://api.github.com/repos/plone/plone.restapi/issues/39"},"comments":{"href":"https://api.github.com/repos/plone/plone.restapi/issues/39/comments"},"review_comments":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/39/comments"},"review_comment":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/plone/plone.restapi/pulls/39/commits"},"statuses":{"href":"https://api.github.com/repos/plone/plone.restapi/statuses/7ede747cea15a40e155d768a4931c4f2db3e0e44"}}}}