protected function doMapping($source, $targetType, PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath)
    {
        $targetTypeWithoutNull = TypeHandling::stripNullableType($targetType);
        $isNullableType = $targetType !== $targetTypeWithoutNull;
        if ($source === null && $isNullableType) {
            return null;
        }
        $targetType = $targetTypeWithoutNull;

        if (is_object($source)) {
            $targetClass = TypeHandling::truncateElementType($targetType);
            if ($source instanceof $targetClass) {
                return $source;
            }
        }

        if ($source === null) {
            $source = '';
        }

        $typeConverter = $this->findTypeConverter($source, $targetType, $configuration);
        $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration);

        if (!is_object($typeConverter) || !($typeConverter instanceof TypeConverterInterface)) {
            throw new Exception\TypeConverterException('Type converter for "' . $source . '" -> "' . $targetType . '" not found.');
        }

        $convertedChildProperties = [];
        foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
            $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
            if ($configuration->shouldSkip($targetPropertyName)) {
                continue;
            }

            if (!$configuration->shouldMap($targetPropertyName)) {
                if ($configuration->shouldSkipUnknownProperties()) {
                    continue;
                }
                throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1335969887);
            }

            $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
            if ($targetPropertyType === null) {
                continue;
            }

            $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);

            $currentPropertyPath[] = $targetPropertyName;
            $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
            array_pop($currentPropertyPath);
            if (!($targetPropertyValue instanceof Error)) {
                $convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
            }
        }
        $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);

        if ($result instanceof Error) {
            $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
        }

        return $result;
    }