diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md
index a04c323d6fe07..525651fce454e 100644
--- a/src/Symfony/Component/Serializer/CHANGELOG.md
+++ b/src/Symfony/Component/Serializer/CHANGELOG.md
@@ -1,11 +1,16 @@
CHANGELOG
=========
+7.3
+---
+
+ * Deprecate the `CompiledClassMetadataFactory` and `CompiledClassMetadataCacheWarmer` classes
+
7.2
---
- * Deprecate the `csv_escape_char` context option of `CsvEncoder` and the `CsvEncoder::ESCAPE_CHAR_KEY` constant
- * Deprecate `CsvEncoderContextBuilder::withEscapeChar()` method
+ * Deprecate the `csv_escape_char` context option of `CsvEncoder`, the `CsvEncoder::ESCAPE_CHAR_KEY` constant
+ and the `CsvEncoderContextBuilder::withEscapeChar()` method, following its deprecation in PHP 8.4
* Add `SnakeCaseToCamelCaseNameConverter`
* Support subclasses of `\DateTime` and `\DateTimeImmutable` for denormalization
* Add the `UidNormalizer::NORMALIZATION_FORMAT_RFC9562` constant
@@ -19,7 +24,6 @@ CHANGELOG
* Add arguments `$class`, `$format` and `$context` to `NameConverterInterface::normalize()` and `NameConverterInterface::denormalize()`
* Add `DateTimeNormalizer::CAST_KEY` context option
- * Add `Default` and "class name" default groups
* Add `AbstractNormalizer::FILTER_BOOL` context option
* Add `CamelCaseToSnakeCaseNameConverter::REQUIRE_SNAKE_CASE_PROPERTIES` context option
* Deprecate `AbstractNormalizerContextBuilder::withDefaultContructorArguments(?array $defaultContructorArguments)`, use `withDefaultConstructorArguments(?array $defaultConstructorArguments)` instead (note the missing `s` character in Contructor word in deprecated method)
diff --git a/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php b/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php
index 0fd1f2f44c364..7a5097e94518b 100644
--- a/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php
+++ b/src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php
@@ -160,4 +160,12 @@ public function withCdataWrappingPattern(?string $cdataWrappingPattern): static
{
return $this->with(XmlEncoder::CDATA_WRAPPING_PATTERN, $cdataWrappingPattern);
}
+
+ /**
+ * Configures whether to ignore empty attributes.
+ */
+ public function withIgnoreEmptyAttributes(?bool $ignoreEmptyAttributes): static
+ {
+ return $this->with(XmlEncoder::IGNORE_EMPTY_ATTRIBUTES, $ignoreEmptyAttributes);
+ }
}
diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
index e1a816380a7b6..ed66fa30898df 100644
--- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
@@ -60,6 +60,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
public const VERSION = 'xml_version';
public const CDATA_WRAPPING = 'cdata_wrapping';
public const CDATA_WRAPPING_PATTERN = 'cdata_wrapping_pattern';
+ public const IGNORE_EMPTY_ATTRIBUTES = 'ignore_empty_attributes';
private array $defaultContext = [
self::AS_COLLECTION => false,
@@ -72,6 +73,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
self::TYPE_CAST_ATTRIBUTES => true,
self::CDATA_WRAPPING => true,
self::CDATA_WRAPPING_PATTERN => '/[<>&]/',
+ self::IGNORE_EMPTY_ATTRIBUTES => false,
];
public function __construct(array $defaultContext = [])
@@ -355,6 +357,13 @@ private function buildXml(\DOMNode $parentNode, mixed $data, string $format, arr
if (\is_bool($data)) {
$data = (int) $data;
}
+
+ if ($context[self::IGNORE_EMPTY_ATTRIBUTES] ?? $this->defaultContext[self::IGNORE_EMPTY_ATTRIBUTES]) {
+ if (null === $data || '' === $data) {
+ continue;
+ }
+ }
+
$parentNode->setAttribute($attributeName, $data);
} elseif ('#' === $key) {
$append = $this->selectNodeType($parentNode, $data, $format, $context);
diff --git a/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php b/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php
index 2f71c6012b222..4175751b08955 100644
--- a/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php
@@ -47,6 +47,7 @@ public function testWithers(array $values)
->withVersion($values[XmlEncoder::VERSION])
->withCdataWrapping($values[XmlEncoder::CDATA_WRAPPING])
->withCdataWrappingPattern($values[XmlEncoder::CDATA_WRAPPING_PATTERN])
+ ->withIgnoreEmptyAttributes($values[XmlEncoder::IGNORE_EMPTY_ATTRIBUTES])
->toArray();
$this->assertSame($values, $context);
@@ -69,6 +70,7 @@ public static function withersDataProvider(): iterable
XmlEncoder::VERSION => '1.0',
XmlEncoder::CDATA_WRAPPING => false,
XmlEncoder::CDATA_WRAPPING_PATTERN => '/[<>&"\']/',
+ XmlEncoder::IGNORE_EMPTY_ATTRIBUTES => true,
]];
yield 'With null values' => [[
@@ -86,6 +88,7 @@ public static function withersDataProvider(): iterable
XmlEncoder::VERSION => null,
XmlEncoder::CDATA_WRAPPING => null,
XmlEncoder::CDATA_WRAPPING_PATTERN => null,
+ XmlEncoder::IGNORE_EMPTY_ATTRIBUTES => null,
]];
}
}
diff --git a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
index 31d2ddfc69c41..ca36554e4b6ad 100644
--- a/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php
@@ -1004,4 +1004,17 @@ private function createXmlWithDateTimeField(): string
', $this->exampleDateTimeString);
}
+
+ public function testEncodeIgnoringEmptyAttribute()
+ {
+ $expected = <<<'XML'
+
+Test
+
+XML;
+
+ $data = ['#' => 'Test', '@attribute' => '', '@attribute2' => null];
+
+ $this->assertEquals($expected, $this->encoder->encode($data, 'xml', ['ignore_empty_attributes' => true]));
+ }
}