From 14d1021c8bbb39e59e5bad75a48f97c177a4fe4e Mon Sep 17 00:00:00 2001
From: Bez Hermoso <bez@activelamp.com>
Date: Tue, 17 Jun 2014 17:05:00 -0700
Subject: [PATCH] Unified data types [actualType and subType]

This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410.

This PR aims to provide a uniform way of declaring data-types of parameters for
parsers and handlers to follow. In turn, this would allow formatters to
determine data-types in a cleaner and less volatile manner. (See use-case that
can be improved with this PR:
https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103)

This is possible by the addition two properties to each property item in
`response`, and `parameters` fields in each API endpoint produced by the
`ApiDocExtractor`:

* `actualType` Contains a value from one of the `DataTypes` class constants.

* `subType` Can contain either `null`, or any other `DataTypes` class constant.
This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein
`subType` would specify the type of the collection items. It is also relevant
when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an
identifier of the model (the FQCN or anything the parser would wish to specify)

Examples:

```php

array(
    'id' => array(
        'dataType' => 'integer',
        'actualType' => DataTypes::INTEGER,
        'subType' => null,
    ),
    'profile' => array(
        'dataType' => 'object (Profile)',
        'actualType' => DataTypes::MODEL,
        'subType' => 'Foo\Entity\Profile',
        'children' => array(
            'name' => array(
                'dataType' => 'string',
                'actualType' => DataTypes::STRING,
                'subType' => null,
             ),
            'birthDate' => array(
                'dataType' => 'date',
                'actualType' => DataTypes::DATE,
                'subType' => null,
            ),
        )
    ),
    'languages' => array(
        'dataType' => 'array of strings',
        'actualType' => DataTypes::COLLECTION,
        'subType' => DataTypes::STRING,
    ),
    'roles' => array(
        'dataType' => 'array of choices',
        'actualType' => DataTypes::COLLECTION,
        'subType' => DataTypes::ENUM,
    ),
    'groups' => array(
        'dataType' => 'array of objects (Group)',
        'actualType' => DataTypes::COLLECTION,
        'subType' => 'Foo\Entity\Group',
    ),
    'profileRevisions' => array(
         'dataType' => 'array of objects (Profile)',
         'actualType' => DataTypes::COLLECTION,
         'subType' => 'Foo\Entity\Profile',
    ),
    'address' => array(
        'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)',
        'actualType' => DataTypes::MODEL,
        'subType' => 'a_type_a_custom_JMS_serializer_handler_handles',
    ),
);
```

When a formatter omits the `dataType` property or leaves it blank, it is
inferred within `ApiDocExtractor` before everything is passed to formatters.
---
 DataTypes.php                           |  63 ++++++++++++
 Extractor/ApiDocExtractor.php           |  58 +++++++++++
 Extractor/Handler/FosRestHandler.php    |  16 ++-
 Formatter/AbstractFormatter.php         |   5 +-
 Parser/FormTypeParser.php               |  42 +++++---
 Parser/JmsMetadataParser.php            |  36 +++++--
 Parser/ValidationParser.php             |  30 ++++++
 Tests/Formatter/SimpleFormatterTest.php | 125 ++++++++++++++++++++++++
 Tests/Parser/FormTypeParserTest.php     |  54 ++++++++++
 Tests/Parser/JmsMetadataParserTest.php  |  57 ++++++++---
 Tests/Parser/ValidationParserTest.php   |  25 +++--
 11 files changed, 467 insertions(+), 44 deletions(-)
 create mode 100644 DataTypes.php

diff --git a/DataTypes.php b/DataTypes.php
new file mode 100644
index 0000000..7e02741
--- /dev/null
+++ b/DataTypes.php
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle.
+ *
+ * (c) Nelmio <hello@nelm.io>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle;
+
+/**
+ * All the supported data-types which will be specified in the `actualType` properties in parameters.
+ *
+ * @author Bez Hermoso <bez@activelamp.com>
+ */
+class DataTypes
+{
+    const INTEGER = 'integer';
+
+    const FLOAT = 'float';
+
+    const STRING = 'string';
+
+    const BOOLEAN = 'boolean';
+
+    const FILE = 'file';
+
+    const ENUM = 'choice';
+
+    const COLLECTION = 'collection';
+
+    const MODEL = 'model';
+
+    const DATE = 'date';
+
+    const DATETIME = 'datetime';
+
+    const TIME = 'time';
+
+    /**
+     * Returns true if the supplied `actualType` value is considered a primitive type. Returns false, otherwise.
+     *
+     * @param  string $type
+     * @return bool
+     */
+    public static function isPrimitive($type)
+    {
+        return in_array(strtolower($type), array(
+            static::INTEGER,
+            static::FLOAT,
+            static::STRING,
+            static::BOOLEAN,
+            static::FILE,
+            static::DATE,
+            static::DATETIME,
+            static::TIME,
+            static::ENUM,
+        ));
+    }
+}
diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php
index 6f051a8..c18c6cd 100644
--- a/Extractor/ApiDocExtractor.php
+++ b/Extractor/ApiDocExtractor.php
@@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
 use Doctrine\Common\Annotations\Reader;
 use Doctrine\Common\Util\ClassUtils;
 use Nelmio\ApiDocBundle\Annotation\ApiDoc;
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Parser\ParserInterface;
 use Nelmio\ApiDocBundle\Parser\PostParserInterface;
 use Symfony\Component\Routing\Route;
@@ -279,6 +280,7 @@ class ApiDocExtractor
             }
 
             $parameters = $this->clearClasses($parameters);
+            $parameters = $this->generateHumanReadableTypes($parameters);
 
             if ('PUT' === $method) {
                 // All parameters are optional with PUT (update)
@@ -302,6 +304,7 @@ class ApiDocExtractor
             }
 
             $response = $this->clearClasses($response);
+            $response = $this->generateHumanReadableTypes($response);
 
             $annotation->setResponse($response);
         }
@@ -414,6 +417,61 @@ class ApiDocExtractor
         return $array;
     }
 
+    /**
+     * Populates the `dataType` properties in the parameter array if empty. Recurses through children when necessary.
+     *
+     * @param  array $array
+     * @return array
+     */
+    protected function generateHumanReadableTypes(array $array)
+    {
+        foreach ($array as $name => $info) {
+
+            if (empty($info['dataType'])) {
+                $array[$name]['dataType'] = $this->generateHumanReadableType($info['actualType'], $info['subType']);
+            }
+
+            if (isset($info['children'])) {
+                $array[$name]['children'] = $this->generateHumanReadableTypes($info['children']);
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * Creates a human-readable version of the `actualType`. `subType` is taken into account.
+     *
+     * @param  string $actualType
+     * @param  string $subType
+     * @return string
+     */
+    protected function generateHumanReadableType($actualType, $subType)
+    {
+        if ($actualType == DataTypes::MODEL) {
+            $parts = explode('\\', $subType);
+
+            return sprintf('object (%s)', end($parts));
+        }
+
+        if ($actualType == DataTypes::COLLECTION) {
+
+            if (DataTypes::isPrimitive($subType)) {
+                return sprintf('array of %ss', $subType);
+            }
+
+            if (class_exists($subType)) {
+                $parts = explode('\\', $subType);
+
+                return sprintf('array of objects (%s)', end($parts));
+            }
+
+            return sprintf('array of objects (%s)', $subType);
+        }
+
+        return $actualType;
+    }
+
     private function getParsers(array $parameters)
     {
         if (isset($parameters['parsers'])) {
diff --git a/Extractor/Handler/FosRestHandler.php b/Extractor/Handler/FosRestHandler.php
index 3165e20..4b7f18f 100644
--- a/Extractor/Handler/FosRestHandler.php
+++ b/Extractor/Handler/FosRestHandler.php
@@ -11,6 +11,7 @@
 
 namespace Nelmio\ApiDocBundle\Extractor\Handler;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Extractor\HandlerInterface;
 use Nelmio\ApiDocBundle\Annotation\ApiDoc;
 use Symfony\Component\Routing\Route;
@@ -28,9 +29,13 @@ class FosRestHandler implements HandlerInterface
     {
         foreach ($annotations as $annot) {
             if ($annot instanceof RequestParam) {
+
+                $requirements = $this->handleRequirements($annot->requirements);
                 $annotation->addParameter($annot->name, array(
                     'required'    => $annot->strict && $annot->default === null,
-                    'dataType'    => $this->handleRequirements($annot->requirements),
+                    'dataType'    => $requirements,
+                    'actualType'  => $this->inferType($requirements),
+                    'subType'     => null,
                     'description' => $annot->description,
                     'readonly'    => false
                 ));
@@ -76,4 +81,13 @@ class FosRestHandler implements HandlerInterface
 
         return (string) $requirements;
     }
+
+    public function inferType($requirement)
+    {
+        if (DataTypes::isPrimitive($requirement)) {
+            return $requirement;
+        }
+
+        return DataTypes::STRING;
+    }
 }
diff --git a/Formatter/AbstractFormatter.php b/Formatter/AbstractFormatter.php
index 94685ed..f84a9a1 100644
--- a/Formatter/AbstractFormatter.php
+++ b/Formatter/AbstractFormatter.php
@@ -12,6 +12,7 @@
 namespace Nelmio\ApiDocBundle\Formatter;
 
 use Nelmio\ApiDocBundle\Annotation\ApiDoc;
+use Nelmio\ApiDocBundle\DataTypes;
 
 abstract class AbstractFormatter implements FormatterInterface
 {
@@ -76,6 +77,8 @@ abstract class AbstractFormatter implements FormatterInterface
                 'format'        => array_key_exists('format', $info) ? $info['format'] : null,
                 'sinceVersion'  => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null,
                 'untilVersion'  => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null,
+                'actualType'    => array_key_exists('actualType', $info) ? $info['actualType'] : null,
+                'subType'    => array_key_exists('subType', $info) ? $info['subType'] : null,
             );
 
             if (isset($info['children']) && (!$info['readonly'] || !$ignoreNestedReadOnly)) {
@@ -100,7 +103,7 @@ abstract class AbstractFormatter implements FormatterInterface
     protected function getNewName($name, $data, $parentName = null)
     {
         $newName = ($parentName) ? sprintf("%s[%s]", $parentName, $name) : $name;
-        $array   = (false === strpos($data['dataType'], "array of")) ? "" : "[]";
+        $array = $data['actualType'] == DataTypes::COLLECTION && $data['subType'] !== null ? '[]' : '';
 
         return sprintf("%s%s", $newName, $array);
     }
diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php
index c87166c..bddfa05 100644
--- a/Parser/FormTypeParser.php
+++ b/Parser/FormTypeParser.php
@@ -11,6 +11,7 @@
 
 namespace Nelmio\ApiDocBundle\Parser;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Symfony\Component\Form\Exception\UnexpectedTypeException;
 use Symfony\Component\Form\Exception\InvalidArgumentException;
 use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
@@ -35,16 +36,16 @@ class FormTypeParser implements ParserInterface
      * @var array
      */
     protected $mapTypes = array(
-        'text'      => 'string',
-        'date'      => 'date',
-        'datetime'  => 'datetime',
-        'checkbox'  => 'boolean',
-        'time'      => 'time',
-        'number'    => 'float',
-        'integer'   => 'int',
-        'textarea'  => 'string',
-        'country'   => 'string',
-        'choice'    => 'choice',
+        'text'      => DataTypes::STRING,
+        'date'      => DataTypes::DATE,
+        'datetime'  => DataTypes::DATETIME,
+        'checkbox'  => DataTypes::BOOLEAN,
+        'time'      => DataTypes::TIME,
+        'number'    => DataTypes::FLOAT,
+        'integer'   => DataTypes::INTEGER,
+        'textarea'  => DataTypes::STRING,
+        'country'   => DataTypes::STRING,
+        'choice'    => DataTypes::ENUM,
     );
 
     public function __construct(FormFactoryInterface $formFactory)
@@ -99,12 +100,18 @@ class FormTypeParser implements ParserInterface
             }
 
             $bestType = '';
+            $actualType = null;
+            $subType = null;
+
             for ($type = $config->getType(); null !== $type; $type = $type->getParent()) {
                 if (isset($this->mapTypes[$type->getName()])) {
                     $bestType = $this->mapTypes[$type->getName()];
+                    $actualType = $bestType;
                 } elseif ('collection' === $type->getName()) {
                     if (is_string($config->getOption('type')) && isset($this->mapTypes[$config->getOption('type')])) {
-                        $bestType = sprintf('array of %ss', $this->mapTypes[$config->getOption('type')]);
+                        $subType = $this->mapTypes[$config->getOption('type')];
+                        $actualType = DataTypes::COLLECTION;
+                        $bestType = sprintf('array of %ss', $subType);
                     } else {
                         // Embedded form collection
                         $subParameters = $this->parseForm($this->formFactory->create($config->getOption('type'), null, $config->getOption('options', array())), $name . '[]');
@@ -139,6 +146,7 @@ class FormTypeParser implements ParserInterface
                         if ($addDefault) {
                             $parameters[$name] = array(
                                 'dataType'      => 'string',
+                                'actualType'      => 'string',
                                 'required'      => $config->getRequired(),
                                 'description'   => $config->getAttribute('description'),
                                 'readonly'      => $config->getDisabled(),
@@ -152,13 +160,15 @@ class FormTypeParser implements ParserInterface
 
             $parameters[$name] = array(
                 'dataType'      => $bestType,
+                'actualType'    => $actualType,
+                'subType'       => $subType,
                 'required'      => $config->getRequired(),
                 'description'   => $config->getAttribute('description'),
                 'readonly'      => $config->getDisabled(),
             );
 
-            switch ($bestType) {
-                case 'datetime':
+            switch ($actualType) {
+                case DataTypes::DATETIME:
                     if (($format = $config->getOption('date_format')) && is_string($format)) {
                         $parameters[$name]['format'] = $format;
                     } elseif ('single_text' == $config->getOption('widget') && $format = $config->getOption('format')) {
@@ -166,15 +176,17 @@ class FormTypeParser implements ParserInterface
                     }
                     break;
 
-                case 'date':
+                case DataTypes::DATE:
                     if (($format = $config->getOption('format')) && is_string($format)) {
                         $parameters[$name]['format'] = $format;
                     }
                     break;
 
-                case 'choice':
+                case DataTypes::ENUM:
                     if ($config->getOption('multiple')) {
                         $parameters[$name]['dataType'] = sprintf('array of %ss', $parameters[$name]['dataType']);
+                        $parameters[$name]['actualType'] = DataTypes::COLLECTION;
+                        $parameters[$name]['subType'] = DataTypes::ENUM;
                     }
 
                     if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {
diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php
index 8af8432..5429769 100644
--- a/Parser/JmsMetadataParser.php
+++ b/Parser/JmsMetadataParser.php
@@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Parser;
 use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
 use JMS\Serializer\SerializationContext;
 use Metadata\MetadataFactoryInterface;
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Util\DocCommentExtractor;
 use JMS\Serializer\Metadata\PropertyMetadata;
 use JMS\Serializer\Metadata\VirtualPropertyMetadata;
@@ -39,6 +40,15 @@ class JmsMetadataParser implements ParserInterface
      */
     private $commentExtractor;
 
+    private $typeMap = array(
+        'integer' => DataTypes::INTEGER,
+        'boolean' => DataTypes::BOOLEAN,
+        'string' => DataTypes::STRING,
+        'float' => DataTypes::FLOAT,
+        'double' => DataTypes::FLOAT,
+        'array' => DataTypes::COLLECTION,
+        'DateTime' => DataTypes::DATETIME,
+    );
     /**
      * Constructor, requires JMS Metadata factory
      */
@@ -120,6 +130,8 @@ class JmsMetadataParser implements ParserInterface
 
                 $params[$name] = array(
                     'dataType'     => $dataType['normalized'],
+                    'actualType'   => $dataType['actualType'],
+                    'subType'      => $dataType['class'],
                     'required'     => false,
                     //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this
                     'description'  => $this->getDescription($item),
@@ -128,7 +140,7 @@ class JmsMetadataParser implements ParserInterface
                     'untilVersion' => $item->untilVersion,
                 );
 
-                if (!is_null($dataType['class'])) {
+                if (!is_null($dataType['class']) && false === $dataType['primitive']) {
                     $params[$name]['class'] = $dataType['class'];
                 }
 
@@ -138,7 +150,7 @@ class JmsMetadataParser implements ParserInterface
                 }
 
                 // check for nested classes with JMS metadata
-                if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
+                if ($dataType['class'] && false === $dataType['primitive'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
                     $visited[]                 = $dataType['class'];
                     $params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups);
                 }
@@ -162,7 +174,9 @@ class JmsMetadataParser implements ParserInterface
             if ($this->isPrimitive($nestedType)) {
                 return array(
                     'normalized' => sprintf("array of %ss", $nestedType),
-                    'class' => null
+                    'actualType' => DataTypes::COLLECTION,
+                    'class' => $this->typeMap[$nestedType],
+                    'primitive' => true,
                 );
             }
 
@@ -170,7 +184,9 @@ class JmsMetadataParser implements ParserInterface
 
             return array(
                 'normalized' => sprintf("array of objects (%s)", end($exp)),
-                'class' => $nestedType
+                'actualType' => DataTypes::COLLECTION,
+                'class' => $nestedType,
+                'primitive' => false,
             );
         }
 
@@ -180,7 +196,9 @@ class JmsMetadataParser implements ParserInterface
         if ($this->isPrimitive($type)) {
             return array(
                 'normalized' => $type,
-                'class' => null
+                'actualType' => $this->typeMap[$type],
+                'class' => null,
+                'primitive' => true,
             );
         }
 
@@ -188,7 +206,9 @@ class JmsMetadataParser implements ParserInterface
         if (!class_exists($type)) {
             return array(
                 'normalized' => sprintf("custom handler result for (%s)", $type),
-                'class' => null
+                'class' => $type,
+                'actualType' => DataTypes::MODEL,
+                'primitive' => false,
             );
         }
 
@@ -197,7 +217,9 @@ class JmsMetadataParser implements ParserInterface
 
         return array(
             'normalized' => sprintf("object (%s)", end($exp)),
-            'class' => $type
+            'class' => $type,
+            'actualType' => DataTypes::MODEL,
+            'primitive' => false,
         );
     }
 
diff --git a/Parser/ValidationParser.php b/Parser/ValidationParser.php
index 1d9e4b8..386b0f9 100644
--- a/Parser/ValidationParser.php
+++ b/Parser/ValidationParser.php
@@ -11,6 +11,7 @@
 
 namespace Nelmio\ApiDocBundle\Parser;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
 use Symfony\Component\Validator\MetadataFactoryInterface;
 use Symfony\Component\Validator\Constraint;
@@ -26,6 +27,21 @@ class ValidationParser implements ParserInterface, PostParserInterface
      */
     protected $factory;
 
+    protected $typeMap = array(
+        'integer' => DataTypes::INTEGER,
+        'int' => DataTypes::INTEGER,
+        'scalar' => DataTypes::STRING,
+        'numeric' => DataTypes::INTEGER,
+        'boolean' => DataTypes::BOOLEAN,
+        'string' => DataTypes::STRING,
+        'float' => DataTypes::FLOAT,
+        'double' => DataTypes::FLOAT,
+        'long' => DataTypes::INTEGER,
+        'object' => DataTypes::MODEL,
+        'array' => DataTypes::COLLECTION,
+        'DateTime' => DataTypes::DATETIME,
+    );
+
     /**
      * Requires a validation MetadataFactory.
      *
@@ -142,6 +158,9 @@ class ValidationParser implements ParserInterface, PostParserInterface
     {
         $class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\'));
 
+        $vparams['actualType'] = DataTypes::STRING;
+        $vparams['subType'] = null;
+
         switch ($class) {
             case 'NotBlank':
                 $vparams['format'][] = '{not blank}';
@@ -149,6 +168,9 @@ class ValidationParser implements ParserInterface, PostParserInterface
                 $vparams['required'] = true;
                 break;
             case 'Type':
+                if (isset($this->typeMap[$constraint->type])) {
+                    $vparams['actualType'] = $this->typeMap[$constraint->type];
+                }
                 $vparams['dataType'] = $constraint->type;
                 break;
             case 'Email':
@@ -162,12 +184,15 @@ class ValidationParser implements ParserInterface, PostParserInterface
                 break;
             case 'Date':
                 $vparams['format'][] = '{Date YYYY-MM-DD}';
+                $vparams['actualType'] = DataTypes::DATE;
                 break;
             case 'DateTime':
                 $vparams['format'][] = '{DateTime YYYY-MM-DD HH:MM:SS}';
+                $vparams['actualType'] = DataTypes::DATETIME;
                 break;
             case 'Time':
                 $vparams['format'][] = '{Time HH:MM:SS}';
+                $vparams['actualType'] = DataTypes::TIME;
                 break;
             case 'Length':
                 $messages = array();
@@ -183,6 +208,8 @@ class ValidationParser implements ParserInterface, PostParserInterface
                 $choices = $this->getChoices($constraint, $className);
                 $format = '[' . join('|', $choices) . ']';
                 if ($constraint->multiple) {
+                    $vparams['actualType'] = DataTypes::COLLECTION;
+                    $vparams['subType'] = DataTypes::ENUM;
                     $messages = array();
                     if (isset($constraint->min)) {
                         $messages[] = "min: {$constraint->min} ";
@@ -192,6 +219,7 @@ class ValidationParser implements ParserInterface, PostParserInterface
                     }
                     $vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}';
                 } else {
+                    $vparams['actualType'] = DataTypes::ENUM;
                     $vparams['format'][] = $format;
                 }
                 break;
@@ -216,6 +244,8 @@ class ValidationParser implements ParserInterface, PostParserInterface
                         }
 
                         $vparams['dataType'] = sprintf("array of objects (%s)", end($exp));
+                        $vparams['actualType'] = DataTypes::COLLECTION;
+                        $vparams['subType'] = $nestedType;
                         $vparams['class'] = $nestedType;
 
                         if (!in_array($nestedType, $visited)) {
diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php
index dfa5b5e..6d8918e 100644
--- a/Tests/Formatter/SimpleFormatterTest.php
+++ b/Tests/Formatter/SimpleFormatterTest.php
@@ -11,6 +11,7 @@
 
 namespace Nelmio\ApiDocBundle\Tests\Formatter;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Tests\WebTestCase;
 
 class SimpleFormatterTest extends WebTestCase
@@ -108,18 +109,24 @@ class SimpleFormatterTest extends WebTestCase
                     array(
                         'a' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => true,
                             'description' => 'A nice description',
                             'readonly' => false,
                         ),
                         'b' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
                         ),
                         'c' => array(
                             'dataType' => 'boolean',
+                            'actualType' => DataTypes::BOOLEAN,
+                            'subType' => null,
                             'required' => true,
                             'description' => '',
                             'readonly' => false,
@@ -149,18 +156,24 @@ class SimpleFormatterTest extends WebTestCase
                     array(
                         'a' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => true,
                             'description' => 'A nice description',
                             'readonly' => false,
                         ),
                         'b' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
                         ),
                         'c' => array(
                             'dataType' => 'boolean',
+                            'actualType' => DataTypes::BOOLEAN,
+                            'subType' => null,
                             'required' => true,
                             'description' => '',
                             'readonly' => false,
@@ -193,6 +206,8 @@ class SimpleFormatterTest extends WebTestCase
                         'dependency_type[a]' =>
                         array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => true,
                             'description' => 'A nice description',
                             'readonly' => false,
@@ -251,6 +266,8 @@ class SimpleFormatterTest extends WebTestCase
                         'foo' =>
                         array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -260,6 +277,8 @@ class SimpleFormatterTest extends WebTestCase
                         'bar' =>
                         array(
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => true,
@@ -269,6 +288,8 @@ class SimpleFormatterTest extends WebTestCase
                         'number' =>
                         array(
                             'dataType' => 'double',
+                            'actualType' => DataTypes::FLOAT,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -278,6 +299,8 @@ class SimpleFormatterTest extends WebTestCase
                         'arr' =>
                         array(
                             'dataType' => 'array',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -287,6 +310,8 @@ class SimpleFormatterTest extends WebTestCase
                         'nested' =>
                         array(
                             'dataType' => 'object (JmsNested)',
+                            'actualType' => DataTypes::MODEL,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -297,6 +322,8 @@ class SimpleFormatterTest extends WebTestCase
                                 'foo' =>
                                 array(
                                     'dataType' => 'DateTime',
+                                    'actualType' => DataTypes::DATETIME,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => true,
@@ -306,6 +333,8 @@ class SimpleFormatterTest extends WebTestCase
                                 'bar' =>
                                 array(
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -315,6 +344,8 @@ class SimpleFormatterTest extends WebTestCase
                                 'baz' =>
                                 array(
                                     'dataType' => 'array of integers',
+                                    'actualType' => DataTypes::COLLECTION,
+                                    'subType' => DataTypes::INTEGER,
                                     'required' => false,
                                     'description' => 'Epic description.
 
@@ -326,6 +357,8 @@ With multiple lines.',
                                 'circular' =>
                                 array(
                                     'dataType' => 'object (JmsNested)',
+                                    'actualType' => DataTypes::MODEL,
+                                    'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -335,6 +368,8 @@ With multiple lines.',
                                 'parent' =>
                                 array(
                                     'dataType' => 'object (JmsTest)',
+                                    'actualType' => DataTypes::MODEL,
+                                    'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest',
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -345,6 +380,8 @@ With multiple lines.',
                                         'foo' =>
                                         array(
                                             'dataType' => 'string',
+                                            'actualType' => DataTypes::STRING,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -354,6 +391,8 @@ With multiple lines.',
                                         'bar' =>
                                         array(
                                             'dataType' => 'DateTime',
+                                            'actualType' => DataTypes::DATETIME,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => true,
@@ -363,6 +402,8 @@ With multiple lines.',
                                         'number' =>
                                         array(
                                             'dataType' => 'double',
+                                            'actualType' => DataTypes::FLOAT,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -372,6 +413,8 @@ With multiple lines.',
                                         'arr' =>
                                         array(
                                             'dataType' => 'array',
+                                            'actualType' => DataTypes::COLLECTION,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -381,6 +424,8 @@ With multiple lines.',
                                         'nested' =>
                                         array(
                                             'dataType' => 'object (JmsNested)',
+                                            'actualType' => DataTypes::MODEL,
+                                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -390,6 +435,8 @@ With multiple lines.',
                                         'nested_array' =>
                                         array(
                                             'dataType' => 'array of objects (JmsNested)',
+                                            'actualType' => DataTypes::COLLECTION,
+                                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -401,6 +448,8 @@ With multiple lines.',
                                 'since' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -410,6 +459,8 @@ With multiple lines.',
                                 'until' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -419,6 +470,8 @@ With multiple lines.',
                                 'since_and_until' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -430,6 +483,8 @@ With multiple lines.',
                         'nested_array' =>
                         array(
                             'dataType' => 'array of objects (JmsNested)',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -452,6 +507,8 @@ With multiple lines.',
                         'dependency_type[a]' =>
                         array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => true,
                             'description' => 'A nice description',
                             'readonly' => false,
@@ -516,6 +573,8 @@ And, it supports multilines until the first \'@\' char.',
                         'foo' =>
                         array (
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -525,6 +584,8 @@ And, it supports multilines until the first \'@\' char.',
                         'bar' =>
                         array (
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => true,
@@ -534,6 +595,8 @@ And, it supports multilines until the first \'@\' char.',
                         'number' =>
                         array (
                             'dataType' => 'double',
+                            'actualType' => DataTypes::FLOAT,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -543,6 +606,8 @@ And, it supports multilines until the first \'@\' char.',
                         'arr' =>
                         array (
                             'dataType' => 'array',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -552,6 +617,8 @@ And, it supports multilines until the first \'@\' char.',
                         'nested' =>
                         array (
                             'dataType' => 'object (JmsNested)',
+                            'actualType' => DataTypes::MODEL,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -562,6 +629,8 @@ And, it supports multilines until the first \'@\' char.',
                                 'foo' =>
                                 array (
                                     'dataType' => 'DateTime',
+                                    'actualType' => DataTypes::DATETIME,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => true,
@@ -571,6 +640,8 @@ And, it supports multilines until the first \'@\' char.',
                                 'bar' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -580,6 +651,8 @@ And, it supports multilines until the first \'@\' char.',
                                 'baz' =>
                                 array (
                                     'dataType' => 'array of integers',
+                                    'actualType' => DataTypes::COLLECTION,
+                                    'subType' => DataTypes::INTEGER,
                                     'required' => false,
                                     'description' => 'Epic description.
 
@@ -591,6 +664,8 @@ With multiple lines.',
                                 'circular' =>
                                 array (
                                     'dataType' => 'object (JmsNested)',
+                                    'actualType' => DataTypes::MODEL,
+                                    'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -600,6 +675,8 @@ With multiple lines.',
                                 'parent' =>
                                 array (
                                     'dataType' => 'object (JmsTest)',
+                                    'actualType' => DataTypes::MODEL,
+                                    'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest',
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -610,6 +687,8 @@ With multiple lines.',
                                         'foo' =>
                                         array (
                                             'dataType' => 'string',
+                                            'actualType' => DataTypes::STRING,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -619,6 +698,8 @@ With multiple lines.',
                                         'bar' =>
                                         array (
                                             'dataType' => 'DateTime',
+                                            'actualType' => DataTypes::DATETIME,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => true,
@@ -628,6 +709,8 @@ With multiple lines.',
                                         'number' =>
                                         array (
                                             'dataType' => 'double',
+                                            'actualType' => DataTypes::FLOAT,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -637,6 +720,8 @@ With multiple lines.',
                                         'arr' =>
                                         array (
                                             'dataType' => 'array',
+                                            'actualType' => DataTypes::COLLECTION,
+                                            'subType' => null,
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -646,6 +731,8 @@ With multiple lines.',
                                         'nested' =>
                                         array (
                                             'dataType' => 'object (JmsNested)',
+                                            'actualType' => DataTypes::MODEL,
+                                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -655,6 +742,8 @@ With multiple lines.',
                                         'nested_array' =>
                                         array (
                                             'dataType' => 'array of objects (JmsNested)',
+                                            'actualType' => DataTypes::COLLECTION,
+                                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                                             'required' => false,
                                             'description' => '',
                                             'readonly' => false,
@@ -666,6 +755,8 @@ With multiple lines.',
                                 'since' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -675,6 +766,8 @@ With multiple lines.',
                                 'until' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -684,6 +777,8 @@ With multiple lines.',
                                 'since_and_until' =>
                                 array (
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'required' => false,
                                     'description' => '',
                                     'readonly' => false,
@@ -695,6 +790,8 @@ With multiple lines.',
                         'nested_array' =>
                         array (
                             'dataType' => 'array of objects (JmsNested)',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -802,6 +899,8 @@ With multiple lines.',
                         array(
                             'required' => true,
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'description' => 'Param1 description.',
                             'readonly' => false,
                         ),
@@ -821,11 +920,15 @@ With multiple lines.',
                     'response' => array (
                         'bar' => array(
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => null,
                             'readonly' => null
                         ),
                         'number' => array(
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -834,17 +937,23 @@ With multiple lines.',
                         ),
                         'objects' => array(
                             'dataType' => 'array of objects (Test)',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
                             'readonly' => null,
                             'required' => null,
                             'children' => array(
                                 'a' => array(
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'format' => '{length: min: foo}, {not blank}',
                                     'required' => true,
                                     'readonly' => null
                                 ),
                                 'b' => array(
                                     'dataType' => 'DateTime',
+                                    'actualType' => DataTypes::DATETIME,
+                                    'subType' => null,
                                     'required' => null,
                                     'readonly' => null
                                 )
@@ -865,18 +974,24 @@ With multiple lines.',
                     array(
                         'a' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => true,
                             'description' => 'A nice description',
                             'readonly' => false,
                         ),
                         'b' => array(
                             'dataType' => 'string',
+                            'actualType' => DataTypes::STRING,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
                         ),
                         'c' => array(
                             'dataType' => 'boolean',
+                            'actualType' => DataTypes::BOOLEAN,
+                            'subType' => null,
                             'required' => true,
                             'description' => '',
                             'readonly' => false,
@@ -893,11 +1008,15 @@ With multiple lines.',
                     'response' => array (
                         'bar' => array(
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => null,
                             'readonly' => null
                         ),
                         'number' => array(
                             'dataType' => 'DateTime',
+                            'actualType' => DataTypes::DATETIME,
+                            'subType' => null,
                             'required' => false,
                             'description' => '',
                             'readonly' => false,
@@ -906,17 +1025,23 @@ With multiple lines.',
                         ),
                         'objects' => array(
                             'dataType' => 'array of objects (Test)',
+                            'actualType' => DataTypes::COLLECTION,
+                            'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
                             'readonly' => null,
                             'required' => null,
                             'children' => array(
                                 'a' => array(
                                     'dataType' => 'string',
+                                    'actualType' => DataTypes::STRING,
+                                    'subType' => null,
                                     'format' => '{length: min: foo}, {not blank}',
                                     'required' => true,
                                     'readonly' => null
                                 ),
                                 'b' => array(
                                     'dataType' => 'DateTime',
+                                    'actualType' => DataTypes::DATETIME,
+                                    'subType' => null,
                                     'required' => null,
                                     'readonly' => null
                                 )
diff --git a/Tests/Parser/FormTypeParserTest.php b/Tests/Parser/FormTypeParserTest.php
index eb7bdb0..cf9c872 100644
--- a/Tests/Parser/FormTypeParserTest.php
+++ b/Tests/Parser/FormTypeParserTest.php
@@ -1,6 +1,7 @@
 <?php
 namespace NelmioApiDocBundle\Tests\Parser;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Form\Extension\DescriptionFormTypeExtension;
 use Nelmio\ApiDocBundle\Parser\FormTypeParser;
 use Nelmio\ApiDocBundle\Tests\Fixtures;
@@ -37,18 +38,24 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 array(
                     'a' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => 'A nice description',
                         'readonly' => false
                     ),
                     'b' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'c' => array(
                         'dataType' => 'boolean',
+                        'actualType' => DataTypes::BOOLEAN,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
@@ -60,24 +67,33 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 array(
                     'collection_type[a]' => array(
                         'dataType' => 'array of strings',
+                        'actualType' => DataTypes::COLLECTION,
+
+                        'subType' => DataTypes::STRING,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'collection_type[b][][a]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => 'A nice description',
                         'readonly' => false
                     ),
                     'collection_type[b][][b]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'collection_type[b][][c]' => array(
                         'dataType' => 'boolean',
+                        'actualType' => DataTypes::BOOLEAN,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
@@ -92,24 +108,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 array(
                     'a' => array(
                         'dataType' => 'array of strings',
+                        'actualType' => DataTypes::COLLECTION,
+                        'subType' => DataTypes::STRING,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'b[][a]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => 'A nice description',
                         'readonly' => false
                     ),
                     'b[][b]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'b[][c]' => array(
                         'dataType' => 'boolean',
+                        'actualType' => DataTypes::BOOLEAN,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
@@ -124,24 +148,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 array(
                     'a' => array(
                         'dataType' => 'array of strings',
+                        'actualType' => DataTypes::COLLECTION,
+                        'subType' => DataTypes::STRING,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'b[][a]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => 'A nice description',
                         'readonly' => false
                     ),
                     'b[][b]' => array(
                         'dataType' => 'string',
+                        'actualType' => DataTypes::STRING,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
                     ),
                     'b[][c]' => array(
                         'dataType' => 'boolean',
+                        'actualType' => DataTypes::BOOLEAN,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false
@@ -153,6 +185,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                 array(
                     'dt1' => array(
                         'dataType' => 'datetime',
+                        'actualType' => DataTypes::DATETIME,
+                        'subType' => null,
                         'required' => true,
                         'description' => 'A nice description',
                         'readonly' => false,
@@ -160,6 +194,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'dt2' => array(
                         'dataType' => 'datetime',
+                        'actualType' => DataTypes::DATETIME,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
@@ -167,6 +203,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'dt3' => array(
                         'dataType' => 'datetime',
+                        'actualType' => DataTypes::DATETIME,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
@@ -174,24 +212,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'dt4' => array(
                         'dataType' => 'datetime',
+                        'actualType' => DataTypes::DATETIME,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
                     ),
                     'dt5' => array(
                         'dataType' => 'datetime',
+                        'actualType' => DataTypes::DATETIME,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
                     ),
                     'd1' => array(
                         'dataType' => 'date',
+                        'actualType' => DataTypes::DATE,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
                     ),
                     'd2' => array(
                         'dataType' => 'date',
+                        'actualType' => DataTypes::DATE,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
@@ -199,6 +245,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'c1' => array(
                         'dataType' => 'choice',
+                        'actualType' => DataTypes::ENUM,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
@@ -206,6 +254,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'c2' => array(
                         'dataType' => 'array of choices',
+                        'actualType' => DataTypes::COLLECTION,
+                        'subType' => DataTypes::ENUM,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
@@ -213,12 +263,16 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
                     ),
                     'c3' => array(
                         'dataType' => 'choice',
+                        'actualType' => DataTypes::ENUM,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
                     ),
                     'c4' => array(
                         'dataType' => 'choice',
+                        'actualType' => DataTypes::ENUM,
+                        'subType' => null,
                         'required' => true,
                         'description' => '',
                         'readonly' => false,
diff --git a/Tests/Parser/JmsMetadataParserTest.php b/Tests/Parser/JmsMetadataParserTest.php
index 880596f..d4640d5 100644
--- a/Tests/Parser/JmsMetadataParserTest.php
+++ b/Tests/Parser/JmsMetadataParserTest.php
@@ -1,6 +1,7 @@
 <?php
 namespace NelmioApiDocBundle\Tests\Parser;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested;
 use Nelmio\ApiDocBundle\Parser\JmsMetadataParser;
 use JMS\Serializer\Metadata\ClassMetadata;
@@ -82,6 +83,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
             array(
                 'foo' => array(
                     'dataType'     => 'DateTime',
+                    'actualType' => DataTypes::DATETIME,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -90,6 +93,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'bar' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -98,6 +103,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'baz' => array(
                     'dataType'     => 'array of integers',
+                    'actualType' => DataTypes::COLLECTION,
+                    'subType' => DataTypes::INTEGER,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -155,6 +162,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
             array(
                 'foo' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -163,6 +172,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'bar' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -171,6 +182,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'baz' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -193,6 +206,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
             array(
                 'foo' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -201,6 +216,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'bar' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -223,6 +240,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
             array(
                 'bar' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -231,6 +250,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'baz' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -253,6 +274,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
             array(
                 'foo' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -261,6 +284,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'bar' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -269,6 +294,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
                 ),
                 'baz' => array(
                     'dataType'     => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
                     'required'     => false,
                     'description'  => null,
                     'readonly'     => false,
@@ -325,26 +352,32 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(
             array(
                 'foo' => array(
-                    'dataType'     => 'string',
-                    'required'     => false,
-                    'description'  => null,
-                    'readonly'     => false,
+                    'dataType' => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
+                    'required' => false,
+                    'description' => null,
+                    'readonly' => false,
                     'sinceVersion' => null,
                     'untilVersion' => null,
                 ),
                 'bar' => array(
-                    'dataType'     => 'string',
-                    'required'     => false,
-                    'description'  => null,
-                    'readonly'     => false,
+                    'dataType' => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
+                    'required' => false,
+                    'description' => null,
+                    'readonly' => false,
                     'sinceVersion' => '2.0',
                     'untilVersion' => null,
                 ),
                 'baz' => array(
-                    'dataType'     => 'string',
-                    'required'     => false,
-                    'description'  => null,
-                    'readonly'     => false,
+                    'dataType' => 'string',
+                    'actualType' => DataTypes::STRING,
+                    'subType' => null,
+                    'required' => false,
+                    'description' => null,
+                    'readonly' => false,
                     'sinceVersion' => null,
                     'untilVersion' => '3.0',
                 )
diff --git a/Tests/Parser/ValidationParserTest.php b/Tests/Parser/ValidationParserTest.php
index 13885b7..0bca5b5 100644
--- a/Tests/Parser/ValidationParserTest.php
+++ b/Tests/Parser/ValidationParserTest.php
@@ -1,6 +1,7 @@
 <?php
 namespace NelmioApiDocBundle\Tests\Parser;
 
+use Nelmio\ApiDocBundle\DataTypes;
 use Nelmio\ApiDocBundle\Tests\WebTestCase;
 use Nelmio\ApiDocBundle\Parser\ValidationParser;
 use Nelmio\ApiDocBundle\Parser\ValidationParserLegacy;
@@ -28,7 +29,6 @@ class ValidationParserTest extends WebTestCase
     public function testParser($property, $expected)
     {
         $result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest'));
-
         foreach ($expected as $name => $value) {
             $this->assertArrayHasKey($property, $result);
             $this->assertArrayHasKey($name, $result[$property]);
@@ -66,25 +66,29 @@ class ValidationParserTest extends WebTestCase
             array(
                 'property' => 'type',
                 'expected' => array(
-                    'dataType' => 'DateTime'
+                    'dataType' => 'DateTime',
+                    'actualType' => DataTypes::DATETIME,
                 )
             ),
             array(
                 'property' => 'date',
                 'expected' => array(
-                    'format' => '{Date YYYY-MM-DD}'
+                    'format' => '{Date YYYY-MM-DD}',
+                    'actualType' => DataTypes::DATE,
                 )
             ),
             array(
                 'property' => 'dateTime',
                 'expected' => array(
-                    'format' => '{DateTime YYYY-MM-DD HH:MM:SS}'
+                    'format' => '{DateTime YYYY-MM-DD HH:MM:SS}',
+                    'actualType' => DataTypes::DATETIME,
                 )
             ),
             array(
                 'property' => 'time',
                 'expected' => array(
-                    'format' => '{Time HH:MM:SS}'
+                    'format' => '{Time HH:MM:SS}',
+                    'actualType' => DataTypes::TIME,
                 )
             ),
             array(
@@ -108,19 +112,24 @@ class ValidationParserTest extends WebTestCase
             array(
                 'property' => 'singlechoice',
                 'expected' => array(
-                    'format' => '[a|b]'
+                    'format' => '[a|b]',
+                    'actualType' => DataTypes::ENUM,
                 )
             ),
             array(
                 'property' => 'multiplechoice',
                 'expected' => array(
-                    'format' => '{choice of [x|y|z]}'
+                    'format' => '{choice of [x|y|z]}',
+                    'actualType' => DataTypes::COLLECTION,
+                    'subType' => DataTypes::ENUM,
                 )
             ),
             array(
                 'property' => 'multiplerangechoice',
                 'expected' => array(
-                    'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}'
+                    'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}',
+                    'actualType' => DataTypes::COLLECTION,
+                    'subType' => DataTypes::ENUM,
                 )
             ),
             array(