diff --git a/src/GoodPhp/Serialization/TypeAdapter/Exception/CollectionItemMappingException.php b/src/GoodPhp/Serialization/TypeAdapter/Exception/CollectionItemMappingException.php index d9f2124..0904ce4 100644 --- a/src/GoodPhp/Serialization/TypeAdapter/Exception/CollectionItemMappingException.php +++ b/src/GoodPhp/Serialization/TypeAdapter/Exception/CollectionItemMappingException.php @@ -2,6 +2,9 @@ namespace GoodPhp\Serialization\TypeAdapter\Exception; +use Exception; +use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\Property\BoundClassProperty; +use GoodPhp\Serialization\TypeAdapter\Primitive\ClassProperties\PropertyMappingException; use RuntimeException; use Throwable; @@ -13,4 +16,17 @@ public function __construct( ) { parent::__construct("Could not map item at key '{$key}': {$previous->getMessage()}", 0, $previous); } + + public static function rethrow(int|string $key, callable $callback): mixed + { + try { + return $callback(); + } catch (PropertyMappingException $e) { + throw new self($key . '.' . $e->path, $e->getPrevious()); + } catch (CollectionItemMappingException $e) { + throw new self($key . '.' . $e->key, $e->getPrevious()); + } catch (Exception $e) { + throw new self($key, $e); + } + } } diff --git a/src/GoodPhp/Serialization/TypeAdapter/Primitive/BuiltIn/ArrayMapper.php b/src/GoodPhp/Serialization/TypeAdapter/Primitive/BuiltIn/ArrayMapper.php index 50cfc01..652751f 100644 --- a/src/GoodPhp/Serialization/TypeAdapter/Primitive/BuiltIn/ArrayMapper.php +++ b/src/GoodPhp/Serialization/TypeAdapter/Primitive/BuiltIn/ArrayMapper.php @@ -25,13 +25,14 @@ public function to(array $value, Type $type, Serializer $serializer): array { $itemAdapter = $serializer->adapter(PrimitiveTypeAdapter::class, $type->arguments[1]); - return MultipleMappingException::map($value, false, function (mixed $item, string|int $key) use ($itemAdapter) { - try { - return $itemAdapter->serialize($item); - } catch (Exception $e) { - throw new CollectionItemMappingException($key, $e); - } - }); + return MultipleMappingException::map( + $value, + false, + fn (mixed $item, string|int $key) => CollectionItemMappingException::rethrow( + $key, + fn () => $itemAdapter->serialize($item) + ) + ); } /** @@ -46,12 +47,13 @@ public function from(array $value, Type $type, Serializer $serializer): array { $itemAdapter = $serializer->adapter(PrimitiveTypeAdapter::class, $type->arguments[1]); - return MultipleMappingException::map($value, false, function (mixed $item, string|int $key) use ($itemAdapter) { - try { - return $itemAdapter->deserialize($item); - } catch (Exception $e) { - throw new CollectionItemMappingException($key, $e); - } - }); + return MultipleMappingException::map( + $value, + false, + fn (mixed $item, string|int $key) => CollectionItemMappingException::rethrow( + $key, + fn () => $itemAdapter->deserialize($item) + ) + ); } } diff --git a/tests/Integration/JsonSerializationTest.php b/tests/Integration/JsonSerializationTest.php index 06d17e3..091c78c 100644 --- a/tests/Integration/JsonSerializationTest.php +++ b/tests/Integration/JsonSerializationTest.php @@ -496,5 +496,11 @@ public function deserializesWithAnExceptionProvider(): Generator ), '{"primitive":1,"nested":{"Field":123},"date":"2020-01-01T00:00:00.000+00:00","nullable":null,"carbonImmutable":"2020-01-01T00:00:00.000000Z"}', ]; + + yield 'ClassStub with wrong nested array field type' => [ + new PropertyMappingException('date.0.Field', new UnexpectedTypeException(123, PrimitiveType::string())), + NamedType::wrap(ClassStub::class, [PrimitiveType::array(NestedStub::class)]), + '{"primitive":1,"nested":{"Field":"something"},"date":[{"Field":123}],"nullable":null,"carbonImmutable":"2020-01-01T00:00:00.000000Z"}', + ]; } }