diff --git a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
index 7333e8015a31..64626084eecb 100644
--- a/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/CsvEncoder.php
@@ -69,7 +69,7 @@ public function encode($data, $format, array $context = [])
{
$handle = fopen('php://temp,', 'w+');
- if (!\is_array($data)) {
+ if (!is_iterable($data)) {
$data = [[$data]];
} elseif (empty($data)) {
$data = [[]];
@@ -210,10 +210,10 @@ public function supportsDecoding($format)
/**
* Flattens an array and generates keys including the path.
*/
- private function flatten(array $array, array &$result, string $keySeparator, string $parentKey = '', bool $escapeFormulas = false)
+ private function flatten(iterable $array, array &$result, string $keySeparator, string $parentKey = '', bool $escapeFormulas = false)
{
foreach ($array as $key => $value) {
- if (\is_array($value)) {
+ if (is_iterable($value)) {
$this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas);
} else {
if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), $this->formulasStartCharacters, true)) {
@@ -245,7 +245,7 @@ private function getCsvOptions(array $context): array
/**
* @return string[]
*/
- private function extractHeaders(array $data): array
+ private function extractHeaders(iterable $data): array
{
$headers = [];
$flippedHeaders = [];
diff --git a/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php b/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php
index dc0bf7fe416d..d17ba6b3557c 100644
--- a/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/YamlEncoder.php
@@ -14,6 +14,7 @@
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Yaml\Yaml;
/**
* Encodes YAML data.
@@ -25,6 +26,8 @@ class YamlEncoder implements EncoderInterface, DecoderInterface
const FORMAT = 'yaml';
private const ALTERNATIVE_FORMAT = 'yml';
+ public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects';
+
private $dumper;
private $parser;
private $defaultContext = ['yaml_inline' => 0, 'yaml_indent' => 0, 'yaml_flags' => 0];
@@ -47,6 +50,10 @@ public function encode($data, $format, array $context = [])
{
$context = array_merge($this->defaultContext, $context);
+ if (isset($context[self::PRESERVE_EMPTY_OBJECTS])) {
+ $context['yaml_flags'] |= Yaml::DUMP_OBJECT_AS_MAP;
+ }
+
return $this->dumper->dump($data, $context['yaml_inline'], $context['yaml_indent'], $context['yaml_flags']);
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index 61311817de80..b4195230fbdb 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -88,6 +88,8 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
*/
public const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';
+ public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects';
+
private $propertyTypeExtractor;
private $typesCache = [];
private $attributesCache = [];
@@ -206,6 +208,10 @@ public function normalize($object, $format = null, array $context = [])
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format)), $class, $format, $context);
}
+ if (isset($context[self::PRESERVE_EMPTY_OBJECTS]) && !\count($data)) {
+ return new \ArrayObject();
+ }
+
return $data;
}
diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
index 02a211858492..619f2fee31d8 100644
--- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
+++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php
@@ -30,7 +30,7 @@ interface NormalizerInterface
* @param string $format Format the normalization result will be encoded as
* @param array $context Context options for the normalizer
*
- * @return array|string|int|float|bool
+ * @return array|string|int|float|bool|\ArrayObject \ArrayObject is used to make sure an empty object is encoded as an object not an array
*
* @throws InvalidArgumentException Occurs when the object given is not an attempted type for the normalizer
* @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
index 0f93a99cd932..f770535456e5 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php
@@ -339,6 +339,43 @@ public function testEncodeWithoutHeader()
]));
}
+ public function testEncodeArrayObject()
+ {
+ $value = new \ArrayObject(['foo' => 'hello', 'bar' => 'hey ho']);
+
+ $this->assertEquals(<<<'CSV'
+foo,bar
+hello,"hey ho"
+
+CSV
+ , $this->encoder->encode($value, 'csv'));
+
+ $value = new \ArrayObject();
+
+ $this->assertEquals("\n", $this->encoder->encode($value, 'csv'));
+ }
+
+ public function testEncodeNestedArrayObject()
+ {
+ $value = new \ArrayObject(['foo' => new \ArrayObject(['nested' => 'value']), 'bar' => new \ArrayObject(['another' => 'word'])]);
+
+ $this->assertEquals(<<<'CSV'
+foo.nested,bar.another
+value,word
+
+CSV
+ , $this->encoder->encode($value, 'csv'));
+ }
+
+ public function testEncodeEmptyArrayObject()
+ {
+ $value = new \ArrayObject();
+ $this->assertEquals("\n", $this->encoder->encode($value, 'csv'));
+
+ $value = ['foo' => new \ArrayObject()];
+ $this->assertEquals("\n\n", $this->encoder->encode($value, 'csv'));
+ }
+
public function testSupportsDecoding()
{
$this->assertTrue($this->encoder->supportsDecoding('csv'));
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php
index c79b9bd945dd..0ddaf79e956c 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/JsonEncodeTest.php
@@ -46,6 +46,8 @@ public function encodeProvider()
return [
[[], '[]', []],
[[], '{}', ['json_encode_options' => JSON_FORCE_OBJECT]],
+ [new \ArrayObject(), '{}', []],
+ [new \ArrayObject(['foo' => 'bar']), '{"foo":"bar"}', []],
];
}
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
index 8a48af7d782f..55da0933ebf3 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
@@ -48,6 +48,26 @@ public function testEncodeScalar()
$this->assertEquals($expected, $this->encoder->encode($obj, 'xml'));
}
+ public function testEncodeArrayObject()
+ {
+ $obj = new \ArrayObject(['foo' => 'bar']);
+
+ $expected = ''."\n".
+ '
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: