diff --git a/Parser/JsonSerializableParser.php b/Parser/JsonSerializableParser.php
new file mode 100644
index 0000000..e8835ac
--- /dev/null
+++ b/Parser/JsonSerializableParser.php
@@ -0,0 +1,86 @@
+hasMethod('__construct')) {
+ foreach ($ref->getMethod('__construct')->getParameters() as $parameter) {
+ if (!$parameter->isOptional()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function parse(array $item)
+ {
+ /** @var \JsonSerializable $obj */
+ $obj = new $item['class']();
+
+ $encoded = $obj->jsonSerialize();
+ $top = $this->getItemMetaData($encoded);
+
+ return $top['children'];
+ }
+
+ public function getItemMetaData($item)
+ {
+ $type = gettype($item);
+
+ $meta = array(
+ 'dataType' => $type,
+ 'required' => true,
+ 'description' => '',
+ 'readonly' => false
+ );
+
+ if ($type == 'object' && $item instanceof \JsonSerializable) {
+ $meta = $this->getItemMetaData($item->jsonSerialize());
+ $meta['class'] = get_class($item);
+ } elseif (($type == 'object' && $item instanceof \stdClass) || ($type == 'array' && !$this->isSequential($item))) {
+ $meta['dataType'] = 'object';
+ $meta['children'] = array();
+ foreach ($item as $key => $value) {
+ $meta['children'][$key] = $this->getItemMetaData($value);
+ }
+ }
+
+ return $meta;
+ }
+
+ /**
+ * Check for numeric sequential keys, just like the json encoder does
+ * Credit: http://stackoverflow.com/a/25206156/859027
+ *
+ * @param array $arr
+ * @return bool
+ */
+ private function isSequential(array $arr)
+ {
+ for ($i = count($arr) - 1; $i >= 0; $i--) {
+ if (!isset($arr[$i]) && !array_key_exists($i, $arr)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index cc2fe13..12960ce 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -16,6 +16,7 @@
Nelmio\ApiDocBundle\Parser\CollectionParser
Nelmio\ApiDocBundle\Parser\FormErrorsParser
+ Nelmio\ApiDocBundle\Parser\JsonSerializableParser
@@ -68,6 +69,10 @@
+
+
+
+
diff --git a/Tests/Fixtures/Model/JsonSerializableOptionalConstructorTest.php b/Tests/Fixtures/Model/JsonSerializableOptionalConstructorTest.php
new file mode 100644
index 0000000..ad631af
--- /dev/null
+++ b/Tests/Fixtures/Model/JsonSerializableOptionalConstructorTest.php
@@ -0,0 +1,22 @@
+ 123,
+ 'name' => 'My name',
+ 'child' => array(
+ 'value' => array(1, 2, 3)
+ ),
+ 'another' => new JsonSerializableOptionalConstructorTest()
+ );
+ }
+}
diff --git a/Tests/Parser/JsonSerializableParserTest.php b/Tests/Parser/JsonSerializableParserTest.php
new file mode 100644
index 0000000..0c5c1f4
--- /dev/null
+++ b/Tests/Parser/JsonSerializableParserTest.php
@@ -0,0 +1,98 @@
+parser = new JsonSerializableParser();
+ }
+
+ /**
+ * @dataProvider dataTestParser
+ */
+ public function testParser($property, $expected)
+ {
+ $result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableTest'));
+ foreach ($expected as $name => $value) {
+ $this->assertArrayHasKey($property, $result);
+ $this->assertArrayHasKey($name, $result[$property]);
+ $this->assertEquals($result[$property][$name], $expected[$name]);
+ }
+ }
+
+ /**
+ * @dataProvider dataTestSupports
+ */
+ public function testSupports($class, $expected)
+ {
+ $this->assertEquals($this->parser->supports(array('class' => $class)), $expected);
+ }
+
+ public function dataTestParser()
+ {
+ return array(
+ array(
+ 'property' => 'id',
+ 'expected' => array(
+ 'dataType' => 'integer'
+ )
+ ),
+ array(
+ 'property' => 'name',
+ 'expected' => array(
+ 'dataType' => 'string'
+ )
+ ),
+ array(
+ 'property' => 'child',
+ 'expected' => array(
+ 'dataType' => 'object',
+ 'children' => array(
+ 'value' => array(
+ 'dataType' => 'array',
+ 'required' => true,
+ 'description' => '',
+ 'readonly' => false
+ )
+ )
+ )
+ )
+ );
+ }
+
+ public function dataTestSupports()
+ {
+ return array(
+ array(
+ 'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableTest',
+ 'expected' => true
+ ),
+ array(
+ 'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableRequiredConstructorTest',
+ 'expected' => false
+ ),
+ array(
+ 'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JsonSerializableOptionalConstructorTest',
+ 'expected' => true
+ ),
+ array(
+ 'class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Popo',
+ 'expected' => false
+ )
+ );
+ }
+}