diff --git a/news/2039.bugfix b/news/2039.bugfix
new file mode 100644
index 000000000..3e1bcd08d
--- /dev/null
+++ b/news/2039.bugfix
@@ -0,0 +1 @@
+Add IJsonCompatible converter for IRichTextValue to handle nested RichText fields safely.
diff --git a/src/plone/restapi/serializer/configure.zcml b/src/plone/restapi/serializer/configure.zcml
index 881b0e16f..4d81ac889 100644
--- a/src/plone/restapi/serializer/configure.zcml
+++ b/src/plone/restapi/serializer/configure.zcml
@@ -89,6 +89,7 @@
+
diff --git a/src/plone/restapi/serializer/converters.py b/src/plone/restapi/serializer/converters.py
index 0cb024d07..bcb91b946 100644
--- a/src/plone/restapi/serializer/converters.py
+++ b/src/plone/restapi/serializer/converters.py
@@ -175,6 +175,16 @@ def timedelta_converter(value):
return json_compatible(value.total_seconds())
+@adapter(IRichTextValue)
+@implementer(IJsonCompatible)
+def richtext_converter(value):
+ return {
+ "data": json_compatible(value.raw),
+ "content-type": json_compatible(value.mimeType),
+ "encoding": json_compatible(value.encoding),
+ }
+
+
@adapter(IRichTextValue, IDexterityContent)
@implementer(IContextawareJsonCompatible)
class RichtextDXContextConverter:
diff --git a/src/plone/restapi/tests/test_serializer_converters.py b/src/plone/restapi/tests/test_serializer_converters.py
index 6dd98b651..ffbbf177c 100644
--- a/src/plone/restapi/tests/test_serializer_converters.py
+++ b/src/plone/restapi/tests/test_serializer_converters.py
@@ -4,6 +4,7 @@
from DateTime import DateTime
from persistent.list import PersistentList
from persistent.mapping import PersistentMapping
+from plone.app.textfield.value import RichTextValue
from plone.restapi.serializer.converters import json_compatible
from plone.restapi.testing import PLONE_RESTAPI_DX_INTEGRATION_TESTING
from unittest import TestCase
@@ -218,3 +219,16 @@ def test_i18n_message(self):
def test_missing_value(self):
self.assertEqual(None, json_compatible(Missing.Value))
+
+ def test_richtext_value_nested(self):
+ value = RichTextValue(
+ raw="
foo
", mimeType="text/html", outputMimeType="text/x-html-safe"
+ )
+ self.assertEqual(
+ {
+ "data": "foo
",
+ "content-type": "text/html",
+ "encoding": "utf-8",
+ },
+ json_compatible({"foo": value})["foo"],
+ )