diff --git a/examples/encoders/simpleType/anyXml.php b/examples/encoders/simpleType/anyXml.php new file mode 100644 index 0000000..f893a69 --- /dev/null +++ b/examples/encoders/simpleType/anyXml.php @@ -0,0 +1,48 @@ +addSimpleTypeConverter( + 'http://www.w3.org/2001/XMLSchema', + 'anyXml', + new class implements + ElementAware, XmlEncoder { + public function iso(Context $context): Iso + { + if ($context->type->getMeta()->isAttribute()->unwrapOr(false)) { + return (new ScalarTypeEncoder())->iso($context); + } + + $targetElementName = $context->type->getXmlTargetNodeName(); + return new Iso( + to: static fn (array $data): string => document_encode([$targetElementName => $data]) + ->manipulate(static fn (\DOMDocument $document) => $document->documentElement->setAttributeNS( + VeeWee\Xml\Xmlns\Xmlns::xsi()->value(), + 'xsi:type', + 'custom:type' + )) + ->stringifyDocumentElement(), + from: static fn (string $xml): array => xml_decode($xml)[$targetElementName], + ); + } + } + ); diff --git a/src/Encoder/ElementEncoder.php b/src/Encoder/ElementEncoder.php index c65dcd1..ea46dae 100644 --- a/src/Encoder/ElementEncoder.php +++ b/src/Encoder/ElementEncoder.php @@ -12,7 +12,7 @@ /** * @implements XmlEncoder */ -final class ElementEncoder implements XmlEncoder +final class ElementEncoder implements Feature\ElementAware, XmlEncoder { /** * @param XmlEncoder $typeEncoder diff --git a/src/Encoder/Feature/ElementAware.php b/src/Encoder/Feature/ElementAware.php new file mode 100644 index 0000000..4831048 --- /dev/null +++ b/src/Encoder/Feature/ElementAware.php @@ -0,0 +1,11 @@ + */ -final class ObjectEncoder implements XmlEncoder +final class ObjectEncoder implements Feature\ElementAware, XmlEncoder { /** * @param class-string $className diff --git a/src/Encoder/OptionalElementEncoder.php b/src/Encoder/OptionalElementEncoder.php index 4c6f24b..edeaef9 100644 --- a/src/Encoder/OptionalElementEncoder.php +++ b/src/Encoder/OptionalElementEncoder.php @@ -13,7 +13,7 @@ * @template T of mixed * @implements XmlEncoder */ -final class OptionalElementEncoder implements Feature\OptionalAware, XmlEncoder +final class OptionalElementEncoder implements Feature\ElementAware, Feature\OptionalAware, XmlEncoder { /** * @param XmlEncoder $elementEncoder diff --git a/src/Encoder/RepeatingElementEncoder.php b/src/Encoder/RepeatingElementEncoder.php index 445fc82..34648a3 100644 --- a/src/Encoder/RepeatingElementEncoder.php +++ b/src/Encoder/RepeatingElementEncoder.php @@ -14,7 +14,7 @@ * @template T * @implements XmlEncoder|null, string> */ -final class RepeatingElementEncoder implements Feature\ListAware, XmlEncoder +final class RepeatingElementEncoder implements Feature\ElementAware, Feature\ListAware, XmlEncoder { /** * @param XmlEncoder $typeEncoder diff --git a/src/Encoder/SimpleType/EncoderDetector.php b/src/Encoder/SimpleType/EncoderDetector.php index c7a7193..2c12423 100644 --- a/src/Encoder/SimpleType/EncoderDetector.php +++ b/src/Encoder/SimpleType/EncoderDetector.php @@ -39,9 +39,11 @@ public function __invoke(Context $context): XmlEncoder } if ($meta->isElement()->unwrapOr(false)) { - $encoder = new ElementEncoder($encoder); + if (!$encoder instanceof Feature\ElementAware) { + $encoder = new ElementEncoder($encoder); + } - if ($meta->isNullable()->unwrapOr(false)) { + if ($meta->isNullable()->unwrapOr(false) && !$encoder instanceof Feature\OptionalAware) { $encoder = new OptionalElementEncoder($encoder); } } diff --git a/tests/Unit/ContextCreatorTrait.php b/tests/Unit/ContextCreatorTrait.php index aa1160a..a5db820 100644 --- a/tests/Unit/ContextCreatorTrait.php +++ b/tests/Unit/ContextCreatorTrait.php @@ -22,7 +22,8 @@ trait ContextCreatorTrait { public static function createContext( XsdType $currentType, - TypeCollection $allTypes = new TypeCollection() + TypeCollection $allTypes = new TypeCollection(), + ?EncoderRegistry $encoderRegistry = null ): Context { return new Context( $currentType, @@ -30,7 +31,7 @@ public static function createContext( $allTypes, new MethodCollection(), ), - EncoderRegistry::default(), + $encoderRegistry ?? EncoderRegistry::default(), self::buildNamespaces(), ); } diff --git a/tests/Unit/Encoder/Feature/ElementAwareEncoderTest.php b/tests/Unit/Encoder/Feature/ElementAwareEncoderTest.php new file mode 100644 index 0000000..9c8426a --- /dev/null +++ b/tests/Unit/Encoder/Feature/ElementAwareEncoderTest.php @@ -0,0 +1,53 @@ +addSimpleTypeConverter( + Xmlns::xsd()->value(), + 'anyType', + new class implements + ElementAware, XmlEncoder { + public function iso(Context $context): Iso + { + $typeName = $context->type->getXmlTargetNodeName(); + return new Iso( + to: static fn (array $data): string => document_encode([$typeName => $data])->stringifyDocumentElement(), + from: static fn (string $xml): array => xml_decode($xml)[$typeName], + ); + } + } + ); + + $context = self::createContext( + XsdType::any()->withXmlTargetNodeName('data'), + encoderRegistry: $registry + ); + $encoder = $registry->detectEncoderForContext($context); + + yield 'element-aware-simple-type' => [ + 'encoder' => $encoder, + 'context' => $context, + 'xml' => 'value', + 'data' => ['key' => 'value'], + ]; + } +}