Skip to content

Commit 03e0318

Browse files
committed
feature symfony#54663 [Serializer] Add XmlEncoder::CDATA_WRAPPING_PATTERN context option (alexpozzi)
This PR was merged into the 7.1 branch. Discussion ---------- [Serializer] Add `XmlEncoder::CDATA_WRAPPING_PATTERN` context option | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix symfony#54155 | License | MIT First of all thank you for all your hard work! This PR adds the ability to configure the CDATA wrapping pattern to give more flexibility on when to wrap values in a CDATA section. For example, XML validators are not allowing double and single quotes outside of a CDATA section, with this change we could be able to change the pattern from `/[<>&]/` to `/[<>&"\']/` and solve that issue without the need of writing a custom XMLEncoder. Commits ------- 8ab57d1 [Serializer] Add XmlEncoder::CDATA_WRAPPING_PATTERN context option
2 parents e2664e8 + 8ab57d1 commit 03e0318

File tree

5 files changed

+61
-7
lines changed

5 files changed

+61
-7
lines changed

src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* Add `AbstractNormalizer::FILTER_BOOL` context option
1111
* Add `CamelCaseToSnakeCaseNameConverter::REQUIRE_SNAKE_CASE_PROPERTIES` context option
1212
* Deprecate `AbstractNormalizerContextBuilder::withDefaultContructorArguments(?array $defaultContructorArguments)`, use `withDefaultConstructorArguments(?array $defaultConstructorArguments)` instead (note the missing `s` character in Contructor word in deprecated method)
13+
* Add `XmlEncoder::CDATA_WRAPPING_PATTERN` context option
1314

1415
7.0
1516
---

src/Symfony/Component/Serializer/Context/Encoder/XmlEncoderContextBuilder.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,12 @@ public function withCdataWrapping(?bool $cdataWrapping): static
152152
{
153153
return $this->with(XmlEncoder::CDATA_WRAPPING, $cdataWrapping);
154154
}
155+
156+
/**
157+
* Configures the pattern used to evaluate if a CDATA section should be added.
158+
*/
159+
public function withCdataWrappingPattern(?string $cdataWrappingPattern): static
160+
{
161+
return $this->with(XmlEncoder::CDATA_WRAPPING_PATTERN, $cdataWrappingPattern);
162+
}
155163
}

src/Symfony/Component/Serializer/Encoder/XmlEncoder.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
5959
public const TYPE_CAST_ATTRIBUTES = 'xml_type_cast_attributes';
6060
public const VERSION = 'xml_version';
6161
public const CDATA_WRAPPING = 'cdata_wrapping';
62+
public const CDATA_WRAPPING_PATTERN = 'cdata_wrapping_pattern';
6263

6364
private array $defaultContext = [
6465
self::AS_COLLECTION => false,
@@ -70,6 +71,7 @@ class XmlEncoder implements EncoderInterface, DecoderInterface, NormalizationAwa
7071
self::ROOT_NODE_NAME => 'response',
7172
self::TYPE_CAST_ATTRIBUTES => true,
7273
self::CDATA_WRAPPING => true,
74+
self::CDATA_WRAPPING_PATTERN => '/[<>&]/',
7375
];
7476

7577
public function __construct(array $defaultContext = [])
@@ -433,7 +435,7 @@ private function appendNode(\DOMNode $parentNode, mixed $data, string $format, a
433435
*/
434436
private function needsCdataWrapping(string $val, array $context): bool
435437
{
436-
return ($context[self::CDATA_WRAPPING] ?? $this->defaultContext[self::CDATA_WRAPPING]) && preg_match('/[<>&]/', $val);
438+
return ($context[self::CDATA_WRAPPING] ?? $this->defaultContext[self::CDATA_WRAPPING]) && preg_match($context[self::CDATA_WRAPPING_PATTERN] ?? $this->defaultContext[self::CDATA_WRAPPING_PATTERN], $val);
437439
}
438440

439441
/**

src/Symfony/Component/Serializer/Tests/Context/Encoder/XmlEncoderContextBuilderTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public function testWithers(array $values)
4646
->withTypeCastAttributes($values[XmlEncoder::TYPE_CAST_ATTRIBUTES])
4747
->withVersion($values[XmlEncoder::VERSION])
4848
->withCdataWrapping($values[XmlEncoder::CDATA_WRAPPING])
49+
->withCdataWrappingPattern($values[XmlEncoder::CDATA_WRAPPING_PATTERN])
4950
->toArray();
5051

5152
$this->assertSame($values, $context);
@@ -67,6 +68,7 @@ public static function withersDataProvider(): iterable
6768
XmlEncoder::TYPE_CAST_ATTRIBUTES => true,
6869
XmlEncoder::VERSION => '1.0',
6970
XmlEncoder::CDATA_WRAPPING => false,
71+
XmlEncoder::CDATA_WRAPPING_PATTERN => '/[<>&"\']/',
7072
]];
7173

7274
yield 'With null values' => [[
@@ -83,6 +85,7 @@ public static function withersDataProvider(): iterable
8385
XmlEncoder::TYPE_CAST_ATTRIBUTES => null,
8486
XmlEncoder::VERSION => null,
8587
XmlEncoder::CDATA_WRAPPING => null,
88+
XmlEncoder::CDATA_WRAPPING_PATTERN => null,
8689
]];
8790
}
8891
}

src/Symfony/Component/Serializer/Tests/Encoder/XmlEncoderTest.php

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,16 +231,56 @@ public function testEncodeRootAttributes()
231231
$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
232232
}
233233

234-
public function testEncodeCdataWrapping()
234+
/**
235+
* @dataProvider encodeCdataWrappingWithDefaultPattern
236+
*/
237+
public function testEncodeCdataWrappingWithDefaultPattern($input, $expected)
235238
{
236-
$array = [
237-
'firstname' => 'Paul & Martha <or Me>',
239+
$this->assertEquals($expected, $this->encoder->encode($input, 'xml'));
240+
}
241+
242+
public static function encodeCdataWrappingWithDefaultPattern()
243+
{
244+
return [
245+
[
246+
['firstname' => 'Paul and Martha'],
247+
'<?xml version="1.0"?>'."\n".'<response><firstname>Paul and Martha</firstname></response>'."\n",
248+
],
249+
[
250+
['lastname' => 'O\'Donnel'],
251+
'<?xml version="1.0"?>'."\n".'<response><lastname>O\'Donnel</lastname></response>'."\n",
252+
],
253+
[
254+
['firstname' => 'Paul & Martha <or Me>'],
255+
'<?xml version="1.0"?>'."\n".'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n",
256+
],
238257
];
258+
}
239259

240-
$expected = '<?xml version="1.0"?>'."\n".
241-
'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n";
260+
/**
261+
* @dataProvider encodeCdataWrappingWithCustomPattern
262+
*/
263+
public function testEncodeCdataWrappingWithCustomPattern($input, $expected)
264+
{
265+
$this->assertEquals($expected, $this->encoder->encode($input, 'xml', ['cdata_wrapping_pattern' => '/[<>&"\']/']));
266+
}
242267

243-
$this->assertEquals($expected, $this->encoder->encode($array, 'xml'));
268+
public static function encodeCdataWrappingWithCustomPattern()
269+
{
270+
return [
271+
[
272+
['firstname' => 'Paul and Martha'],
273+
'<?xml version="1.0"?>'."\n".'<response><firstname>Paul and Martha</firstname></response>'."\n",
274+
],
275+
[
276+
['lastname' => 'O\'Donnel'],
277+
'<?xml version="1.0"?>'."\n".'<response><lastname><![CDATA[O\'Donnel]]></lastname></response>'."\n",
278+
],
279+
[
280+
['firstname' => 'Paul & Martha <or Me>'],
281+
'<?xml version="1.0"?>'."\n".'<response><firstname><![CDATA[Paul & Martha <or Me>]]></firstname></response>'."\n",
282+
],
283+
];
244284
}
245285

246286
public function testEnableCdataWrapping()

0 commit comments

Comments
 (0)