diff --git a/src/PropertyHandler/MixedExporter.php b/src/PropertyHandler/MixedExporter.php index 9ce3bdb..6dfe97b 100644 --- a/src/PropertyHandler/MixedExporter.php +++ b/src/PropertyHandler/MixedExporter.php @@ -15,67 +15,23 @@ use Crell\Serde\Serializer; use Crell\Serde\TypeCategory; +/** + * Exporter/importer for `mixed` properties. + * + * This class makes a good-faith attempt to detect the type of a given field by its value. + * It currently does not work for objects, and on import it works only on array-based + * formats (JSON, YAML, etc.) + */ class MixedExporter implements Importer, Exporter { public function exportValue(Serializer $serializer, Field $field, mixed $value, mixed $runningValue): mixed { $type = \get_debug_type($value); - if ($type === 'array') { - if (array_is_list($value)) { - return $serializer->formatter->serializeSequence($runningValue, $field, $this->arrayToSequence($value), $serializer); - } else { - $dict = $this->arrayToDict($value, $field); - return $serializer->formatter->serializeDictionary($runningValue, $field, $dict, $serializer); - } - } - - return match ($type) { - 'int' => $serializer->formatter->serializeInt($runningValue, $field, $value), - 'float' => $serializer->formatter->serializeFloat($runningValue, $field, $value), - 'bool' => $serializer->formatter->serializeBool($runningValue, $field, $value), - 'string' => $serializer->formatter->serializeString($runningValue, $field, $value), - }; - } - - /** - * @param array $value - */ - protected function arrayToSequence(array $value): Sequence - { - $items = []; - foreach ($value as $k => $v) { - $f = Field::create(serializedName: "$k", phpType: \get_debug_type($v)); - $items[] = new CollectionItem(field: $f, value: $v); - } - return new Sequence($items); - } - - /** - * @param array $value - */ - protected function arrayToDict(array $value, Field $field): Dict - { - /** @var DictionaryField|null $typeField */ - $typeField = $field->typeField; - - $items = []; - foreach ($value as $k => $v) { - // Most $runningValue implementations will be an array. - // Arrays in PHP force-cast an integer-string key to - // an integer. That means we cannot guarantee the type - // of the key going out in the Exporter. The Formatter - // will have to do so, if it cares. However, we can still - // detect and reject string-in-int. - if ($typeField?->keyType === KeyType::Int && \get_debug_type($k) === 'string') { - // It's an int field, but the key is a string. That's a no-no. - throw InvalidArrayKeyType::create($field, 'string'); - } - $f = Field::create(serializedName: "$k", phpType: \get_debug_type($v)); - $items[] = new CollectionItem(field: $f, value: $v); - } - - return new Dict($items); + return $serializer->serialize($value, $runningValue, Field::create( + serializedName: $field->serializedName, + phpType: $type, + )); } public function importValue(Deserializer $deserializer, Field $field, mixed $source): mixed @@ -83,13 +39,12 @@ public function importValue(Deserializer $deserializer, Field $field, mixed $sou // This is normally a bad idea, as the $source should be opaque. In this // case, we're guaranteed that the $source is array-based, so we can introspect // it directly. - return match (\get_debug_type($source[$field->serializedName])) { - 'int' => $deserializer->deformatter->deserializeInt($source, $field), - 'float' => $deserializer->deformatter->deserializeFloat($source, $field), - 'bool' => $deserializer->deformatter->deserializeBool($source, $field), - 'string' => $deserializer->deformatter->deserializeString($source, $field), - 'array' => $deserializer->deformatter->deserializeDictionary($source, $field, $deserializer), - }; + $type = \get_debug_type($source[$field->serializedName]); + + return $deserializer->deserialize($source, Field::create( + serializedName: $field->serializedName, + phpType: $type, + )); } public function canExport(Field $field, mixed $value, string $format): bool diff --git a/tests/SerdeTestCases.php b/tests/SerdeTestCases.php index b795f9d..8cfe446 100644 --- a/tests/SerdeTestCases.php +++ b/tests/SerdeTestCases.php @@ -1235,7 +1235,8 @@ public static function mixed_val_property_examples(): iterable yield 'string' => [new MixedVal('hello')]; yield 'int' => [new MixedVal(5)]; yield 'float' => [new MixedVal(3.14)]; - yield 'array' => [new MixedVal(['a', 'b', 'c'])]; + yield 'sequence' => [new MixedVal(['a', 'b', 'c'])]; + yield 'dict' => [new MixedVal(['a' => 'A', 'b' => 'B', 'c' => 'C'])]; // Objects can't work, because they cannot be imported without type data. // Exporting might. Todo for later. //yield 'object' => [new Point(3, 4, 5)];