diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index e1fc2b4a..d297b0ed 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -436,4 +436,18 @@ abstract public function serializeBool($boolValue); * @return array */ abstract public function toArray(): array; + + /** + * @param array $data + * + * @return array + */ + abstract public function getEncoderContext(array $data): array; + + /** + * @param array $normalizedData + * + * @return array + */ + abstract public function transformData(array $normalizedData): array; } diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index 157364ca..f158db06 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -74,7 +74,7 @@ public function setStatus($statusCode) */ public function visit(mixed $data): Response { - $normalizedData = $this->normalizer->normalize($data); + [$normalizedData, $encoderContext] = $this->normalizer->normalize($data, $this->format); //@todo Needs refactoring! // A hackish solution to enable outer visitors to disable setting @@ -89,7 +89,9 @@ public function visit(mixed $data): Response $response = clone $this->response; - $response->setContent($this->encoder->encode($normalizedData, $this->format)); + $content = $this->encoder->encode($normalizedData, $this->format, $encoderContext); + + $response->setContent($content); // reset the inner response $this->response = new Response(null, Response::HTTP_OK); diff --git a/src/contracts/Output/VisitorAdapterNormalizer.php b/src/contracts/Output/VisitorAdapterNormalizer.php index e4aad5d2..176f7855 100644 --- a/src/contracts/Output/VisitorAdapterNormalizer.php +++ b/src/contracts/Output/VisitorAdapterNormalizer.php @@ -8,6 +8,7 @@ namespace Ibexa\Contracts\Rest\Output; +use Ibexa\Rest\Output\Generator\InMemory\Xml as InMemoryXml; use Ibexa\Rest\Output\Generator\Json; use LogicException; use Symfony\Component\Serializer\Encoder\EncoderInterface; @@ -38,7 +39,7 @@ public function normalize(mixed $object, ?string $format = null, array $context : null; if ($eligibleVisitor instanceof ValueObjectVisitor) { - return $this->visitValueObject($object, $eligibleVisitor); + return $this->visitValueObject($object, $eligibleVisitor, $format); } return $this->normalizer->normalize($object, $format, $context); @@ -79,11 +80,14 @@ public function supportsNormalization(mixed $data, ?string $format = null, array } /** - * @return array + * @return array, array> */ - private function visitValueObject(object $object, ValueObjectVisitor $valueObjectVisitor): array - { - $visitor = $this->createVisitor(); + private function visitValueObject( + object $object, + ValueObjectVisitor $valueObjectVisitor, + string $format + ): array { + $visitor = $this->createVisitor($format); $generator = $visitor->getGenerator(); $generator->reset(); @@ -93,21 +97,26 @@ private function visitValueObject(object $object, ValueObjectVisitor $valueObjec $generator->endDocument($object); - return $generator->toArray(); + $normalizedData = $generator->toArray(); + $encoderContext = $generator->getEncoderContext($normalizedData); + + return [$generator->transformData($normalizedData), $encoderContext]; } - private function createVisitor(): Visitor + private function createVisitor(string $format): Visitor { $fieldTypeHashGenerator = new Json\FieldTypeHashGenerator($this->normalizer); - $generator = new Json($fieldTypeHashGenerator); + $generator = $format === 'xml' + ? new InMemoryXml($fieldTypeHashGenerator) + : new Json($fieldTypeHashGenerator); return new Visitor( $generator, $this->normalizer, $this->encoder, $this->valueObjectVisitorResolver, - 'json', + $format, ); } } diff --git a/src/lib/Output/Generator/InMemory/Xml.php b/src/lib/Output/Generator/InMemory/Xml.php new file mode 100644 index 00000000..d2f0e012 --- /dev/null +++ b/src/lib/Output/Generator/InMemory/Xml.php @@ -0,0 +1,51 @@ +generateMediaTypeWithVendor($name, 'xml', $this->vendor); + } + + /** + * @param string $name + * @param string $value + */ + public function startAttribute($name, $value): void + { + $this->checkStartAttribute($name); + + $this->json->{'@' . $name} = $value; + } + + public function transformData(array $normalizedData): array + { + $topNodeName = array_key_first($normalizedData); + $data = array_filter( + $normalizedData[$topNodeName], + static fn (string $key): bool => str_starts_with($key, '@'), + ARRAY_FILTER_USE_KEY, + ); + $data['#'] = $normalizedData[$topNodeName]; + + return $data; + } + + public function getEncoderContext(array $data): array + { + return [ + XmlEncoder::ROOT_NODE_NAME => array_key_first($data), + ]; + } +} diff --git a/src/lib/Output/Generator/Json.php b/src/lib/Output/Generator/Json.php index 6f32f8a7..5aad23ce 100644 --- a/src/lib/Output/Generator/Json.php +++ b/src/lib/Output/Generator/Json.php @@ -329,4 +329,14 @@ public function toArray(): array { return json_decode(json_encode($this->json, JSON_THROW_ON_ERROR), true, JSON_THROW_ON_ERROR); } + + public function getEncoderContext(array $data): array + { + return []; + } + + public function transformData(array $normalizedData): array + { + return $normalizedData; + } } diff --git a/src/lib/Output/Generator/Xml.php b/src/lib/Output/Generator/Xml.php index 04aa742a..a5bda2cd 100644 --- a/src/lib/Output/Generator/Xml.php +++ b/src/lib/Output/Generator/Xml.php @@ -269,4 +269,14 @@ public function toArray(): array { return []; } + + public function getEncoderContext(array $data): array + { + return []; + } + + public function transformData(array $normalizedData): array + { + return $normalizedData; + } }