diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-90347-EnableRecursiveTransformationOfPropertiesInJsonView.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-90347-EnableRecursiveTransformationOfPropertiesInJsonView.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b8d0a12c4d8abef115d6cfa44a5317d163433e18
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-90347-EnableRecursiveTransformationOfPropertiesInJsonView.rst
@@ -0,0 +1,51 @@
+.. include:: ../../Includes.txt
+
+===========================================================================
+Feature: #90347 - Enable recursive transformation of properties in JsonView
+===========================================================================
+
+See :issue:`90347`
+
+Description
+===========
+
+The Extbase :php:`JsonView` is now able to resolve recursive properties of
+objects, e.g. directories containing directories or comments containing comments
+as replies.
+
+Examples:
+
+1. This is for 1:1 relations, where a comment has at most 1 comment.
+
+.. code-block:: php
+
+   $configuration = [
+       'comment' => [
+           '_recursive' => ['comment']
+        ]
+   ];
+
+
+2. This is for the more common 1:n relation in which you have lists of sub objects.
+
+.. code-block:: php
+
+   $configuration = [
+       'directories' => [
+           '_descendAll' => [
+               '_recursive' => ['directories']
+           ],
+       ]
+   ];
+
+You can put all the other configuration like `_only` or `_exclude` at the same
+level as `_recursive` and the view will apply this for all levels.
+
+Impact
+======
+
+Developers can now use the `_recursive` property in the :php:`JsonView`
+configuration in order to resolve recursive properties instead of defining each
+level manually.
+
+.. index:: ext:extbase
diff --git a/typo3/sysext/extbase/Classes/Mvc/View/JsonView.php b/typo3/sysext/extbase/Classes/Mvc/View/JsonView.php
index 22d93a04e689ca767467e05135a18d8e501095e6..00d705d04e24767b3928fd5de19a065e35aaec97 100644
--- a/typo3/sysext/extbase/Classes/Mvc/View/JsonView.php
+++ b/typo3/sysext/extbase/Classes/Mvc/View/JsonView.php
@@ -48,6 +48,11 @@ class JsonView extends AbstractView
      */
     protected $variablesToRender = ['value'];
 
+    /**
+     * @var string
+     */
+    protected $currentVariable = '';
+
     /**
      * The rendering configuration for this JSON view which
      * determines which properties of each variable to render.
@@ -67,7 +72,7 @@ class JsonView extends AbstractView
      *          '_exclude' => ['secretTitle'],
      *          '_descend' => [
      *              'customer' => [
-     *                  '_only' => [array(]'firstName', 'lastName']
+     *                  '_only' => ['firstName', 'lastName']
      *              ]
      *          ]
      *      ],
@@ -203,17 +208,20 @@ class JsonView extends AbstractView
     protected function renderArray()
     {
         if (count($this->variablesToRender) === 1) {
+            $firstLevel = false;
             $variableName = current($this->variablesToRender);
+            $this->currentVariable = $variableName;
             $valueToRender = $this->variables[$variableName] ?? null;
             $configuration = $this->configuration[$variableName] ?? [];
         } else {
+            $firstLevel = true;
             $valueToRender = [];
             foreach ($this->variablesToRender as $variableName) {
                 $valueToRender[$variableName] = $this->variables[$variableName] ?? null;
             }
             $configuration = $this->configuration;
         }
-        return $this->transformValue($valueToRender, $configuration);
+        return $this->transformValue($valueToRender, $configuration, $firstLevel);
     }
 
     /**
@@ -222,13 +230,17 @@ class JsonView extends AbstractView
      *
      * @param mixed $value The value to transform
      * @param array $configuration Configuration for transforming the value
+     * @param bool $firstLevel
      * @return mixed The transformed value
      */
-    protected function transformValue($value, array $configuration)
+    protected function transformValue($value, array $configuration, $firstLevel = false)
     {
         if (is_array($value) || $value instanceof \ArrayAccess) {
             $array = [];
             foreach ($value as $key => $element) {
+                if ($firstLevel) {
+                    $this->currentVariable = $key;
+                }
                 if (isset($configuration['_descendAll']) && is_array($configuration['_descendAll'])) {
                     $array[$key] = $this->transformValue($element, $configuration['_descendAll']);
                 } else {
@@ -279,6 +291,8 @@ class JsonView extends AbstractView
                 $propertiesToRender[$propertyName] = $propertyValue;
             } elseif (isset($configuration['_descend']) && array_key_exists($propertyName, $configuration['_descend'])) {
                 $propertiesToRender[$propertyName] = $this->transformValue($propertyValue, $configuration['_descend'][$propertyName]);
+            } elseif (isset($configuration['_recursive']) && in_array($propertyName, $configuration['_recursive'])) {
+                $propertiesToRender[$propertyName] = $this->transformValue($propertyValue, $this->configuration[$this->currentVariable]);
             }
         }
         if (isset($configuration['_exposeObjectIdentifier']) && $configuration['_exposeObjectIdentifier'] === true) {
diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/View/JsonViewTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/View/JsonViewTest.php
index 56fdf7bfa11cf9b966c8986accc74745faa2e2e4..bc78fc7ed3cde1e10ffaaef03ff03f1cbbe33985 100644
--- a/typo3/sysext/extbase/Tests/Unit/Mvc/View/JsonViewTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Mvc/View/JsonViewTest.php
@@ -150,12 +150,12 @@ class JsonViewTest extends UnitTestCase
         $dateTimeObject = new \DateTime('2011-02-03T03:15:23', new \DateTimeZone('UTC'));
         $configuration = [];
         $expected = '2011-02-03T03:15:23+00:00';
-        $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in UTC time zone could not be serialized.'];
+        $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in UTC time zone should not be serialized.'];
 
         $dateTimeObject = new \DateTime('2013-08-15T15:25:30', new \DateTimeZone('America/Los_Angeles'));
         $configuration = [];
         $expected = '2013-08-15T15:25:30-07:00';
-        $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in America/Los_Angeles time zone could not be serialized.'];
+        $output[] = [$dateTimeObject, $configuration, $expected, 'DateTime object in America/Los_Angeles time zone should not be serialized.'];
 
         return $output;
     }
@@ -176,6 +176,182 @@ class JsonViewTest extends UnitTestCase
 
         self::assertSame($expected, $actual, $description);
     }
+    /**
+     * data provider for testRecursive()
+     * @return array
+     */
+    public function jsonViewTestDataRecursive(): array
+    {
+        $object = new class('foo') {
+            private $value1 = '';
+            private $child;
+            public function __construct($value1)
+            {
+                $this->value1 = $value1;
+            }
+            public function getValue1()
+            {
+                return $this->value1;
+            }
+            public function setValue1(string $value1)
+            {
+                $this->value1 = $value1;
+            }
+            public function getChild()
+            {
+                return $this->child;
+            }
+            public function setChild($child)
+            {
+                $this->child = $child;
+            }
+        };
+
+        $child1 = clone $object;
+        $child1->setValue1('bar');
+        $child2 = clone $object;
+        $child2->setValue1('baz');
+        $child1->setChild($child2);
+        $object->setChild($child1);
+
+        $configuration = [
+            'testData' => [
+                '_recursive' => ['child']
+            ]
+        ];
+
+        $expected = [
+            'child' => [
+                'child' => [
+                    'child' => null,
+                    'value1' => 'baz'
+                ],
+                'value1' => 'bar',
+            ],
+            'value1' => 'foo',
+        ];
+
+        $output[] = [$object, $configuration, $expected, 'testData', 'Recursive rendering of defined property should be possible.'];
+
+        $object = new class('foo') {
+            private $value1 = '';
+            private $children = [];
+            private $secret = 'secret';
+            public function __construct($value1)
+            {
+                $this->value1 = $value1;
+            }
+            public function getValue1()
+            {
+                return $this->value1;
+            }
+            public function setValue1(string $value1)
+            {
+                $this->value1 = $value1;
+            }
+            public function getChildren()
+            {
+                return $this->children;
+            }
+            public function addChild($child)
+            {
+                $this->children[] = $child;
+            }
+            public function getSecret()
+            {
+                return $this->secret;
+            }
+        };
+        $child1 = clone $object;
+        $child1->setValue1('bar');
+        $child1->addChild(clone $object);
+        $child1->addChild(clone $object);
+
+        $child2 = clone $object;
+        $child2->setValue1('baz');
+        $child2->addChild(clone $object);
+        $child2->addChild(clone $object);
+
+        $object->addChild($child1);
+        $object->addChild($child2);
+        $children = [
+            clone $object,
+            clone $object
+        ];
+
+        $configuration = [
+            'testData' => [
+                '_descendAll' => [
+                    '_exclude' => ['secret'],
+                    '_recursive' => ['children']
+                ],
+            ]
+        ];
+
+        $expected = [
+            [
+                'children' => [
+                    [
+                        'children' => [
+                            ['children' => [], 'value1' => 'foo'],
+                            ['children' => [], 'value1' => 'foo']
+                        ],
+                        'value1' => 'bar'
+                    ],
+                    [
+                        'children' => [
+                            ['children' => [], 'value1' => 'foo'],
+                            ['children' => [], 'value1' => 'foo']
+                        ],
+                        'value1' => 'baz'
+                    ]
+                ],
+                'value1' => 'foo'
+            ],
+            [
+                'children' => [
+                    [
+                        'children' => [
+                            ['children' => [], 'value1' => 'foo'],
+                            ['children' => [], 'value1' => 'foo']
+                        ],
+                        'value1' => 'bar'
+                    ],
+                    [
+                        'children' => [
+                            ['children' => [], 'value1' => 'foo'],
+                            ['children' => [], 'value1' => 'foo']
+                        ],
+                        'value1' => 'baz'
+                    ]
+                ],
+                'value1' => 'foo'
+            ]
+        ];
+        $output[] = [$children, $configuration, $expected, 'testData', 'Recursive rendering of lists of defined property should be possible.'];
+
+        return $output;
+    }
+
+    /**
+     * @test
+     * @param object|array $object
+     * @param array $configuration
+     * @param array|string $expected
+     * @param string $variableToRender
+     * @param string $description
+     * @dataProvider jsonViewTestDataRecursive
+     */
+    public function testRecursive($object, array $configuration, $expected, string $variableToRender, string $description): void
+    {
+        $jsonView = $this->getAccessibleMock(JsonView::class, ['dummy'], [], '', false);
+        $jsonView->_set('configuration', $configuration);
+        $jsonView->_set('variablesToRender', [$variableToRender]);
+        $jsonView->_call('assign', $variableToRender, $object);
+        $actual = $jsonView->_call('renderArray');
+
+        self::assertSame($expected, $actual, $description);
+    }
 
     /**
      * data provider for testTransformValueWithObjectIdentifierExposure()