Skip to content

Commit f1ded59

Browse files
authored
Add attributes to XML tag when XML formatting (#51)
1 parent dff269a commit f1ded59

File tree

3 files changed

+114
-56
lines changed

3 files changed

+114
-56
lines changed

src/Formatter/XmlDataInterface.php

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ interface XmlDataInterface
1717
*/
1818
public function xmlTagName(): string;
1919

20+
/**
21+
* Returns an array of attributes for the XML tag.
22+
*
23+
* The array key is the attribute name, and the value is the attribute value.
24+
*
25+
* @return array<string, string> The attributes of the XML tag.
26+
*/
27+
public function xmlTagAttributes(): array;
28+
2029
/**
2130
* Returns an array of data to format as XML.
2231
*

src/Formatter/XmlDataResponseFormatter.php

+6-15
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ final class XmlDataResponseFormatter implements DataResponseFormatterInterface
2727
use ResponseContentTrait;
2828

2929
private const DEFAULT_ITEM_TAG_NAME = 'item';
30-
private const KEY_ATTRIBUTE_NAME = 'key';
3130

3231
/**
3332
* @var string The Content-Type header for the response.
@@ -112,23 +111,16 @@ private function buildXml(DOMDocument $dom, $element, $data): void
112111
}
113112

114113
if (is_array($data)) {
115-
$dataSize = count($data);
116-
117114
foreach ($data as $name => $value) {
118-
if (is_int($name) && is_object($value)) {
119-
$this->buildObject($dom, $element, $value, $dataSize > 1 ? $name : null);
115+
if (is_object($value)) {
116+
$this->buildObject($dom, $element, $value);
120117
continue;
121118
}
122119

123120
$child = $this->safeCreateDomElement($dom, $name);
124-
125-
if ($dataSize > 1 && is_int($name)) {
126-
$child->setAttribute(self::KEY_ATTRIBUTE_NAME, (string) $name);
127-
}
128-
129121
$element->appendChild($child);
130122

131-
if (is_array($value) || is_object($value)) {
123+
if (is_array($value)) {
132124
$this->buildXml($dom, $child, $value);
133125
continue;
134126
}
@@ -153,9 +145,8 @@ private function buildXml(DOMDocument $dom, $element, $data): void
153145
* @param DOMDocument $dom The root DOM document.
154146
* @param DOMDocument|DOMElement $element The current DOM element being processed.
155147
* @param object $object To build.
156-
* @param int|null $key Key attribute value.
157148
*/
158-
private function buildObject(DOMDocument $dom, $element, object $object, int $key = null): void
149+
private function buildObject(DOMDocument $dom, $element, object $object): void
159150
{
160151
if (!($object instanceof XmlDataInterface)) {
161152
throw new RuntimeException(sprintf(
@@ -167,8 +158,8 @@ private function buildObject(DOMDocument $dom, $element, object $object, int $ke
167158

168159
$child = $this->safeCreateDomElement($dom, $object->xmlTagName());
169160

170-
if ($key !== null) {
171-
$child->setAttribute(self::KEY_ATTRIBUTE_NAME, (string) $key);
161+
foreach ($object->xmlTagAttributes() as $name => $value) {
162+
$child->setAttribute($name, $value);
172163
}
173164

174165
$element->appendChild($child);

tests/Formatter/XmlDataResponseFormatterTest.php

+99-41
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ public function testScalarValues(): void
121121
$this->xml(
122122
<<<EOF
123123
<response>
124-
<item key="0">true</item>
125-
<item key="1">false</item>
126-
<item key="2">100.2</item>
124+
<item>true</item>
125+
<item>false</item>
126+
<item>100.2</item>
127127
</response>
128128
EOF
129129
),
@@ -143,20 +143,20 @@ public function testArrayValues(): void
143143
$this->assertSame(
144144
$this->xml(
145145
<<<EOF
146-
<response>
147-
<item key="0">
148-
<item key="100"/>
149-
<item key="200"/>
150-
<item key="300"/>
151-
</item>
152-
<item key="1">
153-
<item key="0">1</item>
154-
<item key="1">1.1</item>
155-
<foo>bar</foo>
156-
<item key="2">true</item>
157-
<item key="3">false</item>
158-
</item>
159-
</response>
146+
<response>
147+
<item>
148+
<item/>
149+
<item/>
150+
<item/>
151+
</item>
152+
<item>
153+
<item>1</item>
154+
<item>1.1</item>
155+
<foo>bar</foo>
156+
<item>true</item>
157+
<item>false</item>
158+
</item>
159+
</response>
160160
EOF
161161
),
162162
$result->getBody()->getContents()
@@ -171,6 +171,11 @@ public function xmlTagName(): string
171171
return 'empty';
172172
}
173173

174+
public function xmlTagAttributes(): array
175+
{
176+
return [];
177+
}
178+
174179
public function xmlData(): array
175180
{
176181
return [];
@@ -180,7 +185,7 @@ public function xmlData(): array
180185
$result->getBody()->rewind();
181186

182187
$this->assertSame(
183-
$this->xml('<response><object><empty/></object></response>'),
188+
$this->xml('<response><empty/></response>'),
184189
$result->getBody()->getContents()
185190
);
186191
}
@@ -201,7 +206,7 @@ public function testObjectValues(): void
201206
<int>$object->int</int>
202207
<float>$object->float</float>
203208
<array>
204-
<item key="0">1</item>
209+
<item>1</item>
205210
<foo>bar</foo>
206211
</array>
207212
</dummy-class>
@@ -236,26 +241,26 @@ public function testArrayObjectValues(): void
236241
$this->xml(
237242
<<<EOF
238243
<response>
239-
<dummy-class key="0">
244+
<dummy-class>
240245
<string>$object1->string</string>
241246
<int>$object1->int</int>
242247
<float>$object1->float</float>
243248
<array>
244-
<item key="0">foo</item>
245-
<item key="1">1.1</item>
249+
<item>foo</item>
250+
<item>1.1</item>
246251
</array>
247252
</dummy-class>
248-
<dummy-class key="1">
253+
<dummy-class>
249254
<string>$object2->string</string>
250255
<int>$object2->int</int>
251256
<float>$object2->float</float>
252257
<array>
253-
<item key="0">1</item>
254-
<item key="1">2</item>
255-
<item key="2">3</item>
258+
<item>1</item>
259+
<item>2</item>
260+
<item>3</item>
256261
</array>
257262
</dummy-class>
258-
<dummy-class key="2">
263+
<dummy-class>
259264
<string>$object3->string</string>
260265
<int>$object3->int</int>
261266
<float>$object3->float</float>
@@ -268,6 +273,52 @@ public function testArrayObjectValues(): void
268273
);
269274
}
270275

276+
public function testObjectValuesWithAttributes(): void
277+
{
278+
$objects = [
279+
$object1 = $this->createDummyObject('foo', 99, 1.1, [
280+
$object2 = $this->createDummyObject('bar', 10, 2.2, [1, 2, 3], ['attribute' => '25']),
281+
$object3 = $this->createDummyObject('baz', 0, 3.3, ['bar' => 'baz'], ['attribute' => '']),
282+
], ['attribute' => '22']),
283+
];
284+
$dataResponse = $this->createResponse($objects);
285+
$result = (new XmlDataResponseFormatter())->format($dataResponse);
286+
$result->getBody()->rewind();
287+
288+
$this->assertSame(
289+
$this->xml(
290+
<<<EOF
291+
<response>
292+
<dummy-class attribute="22">
293+
<string>$object1->string</string>
294+
<int>$object1->int</int>
295+
<float>$object1->float</float>
296+
<array>
297+
<dummy-class attribute="25">
298+
<string>$object2->string</string>
299+
<int>$object2->int</int>
300+
<float>$object2->float</float>
301+
<array>
302+
<item>1</item>
303+
<item>2</item>
304+
<item>3</item>
305+
</array>
306+
</dummy-class>
307+
<dummy-class attribute="">
308+
<string>$object3->string</string>
309+
<int>$object3->int</int>
310+
<float>$object3->float</float>
311+
<array><bar>baz</bar></array>
312+
</dummy-class>
313+
</array>
314+
</dummy-class>
315+
</response>
316+
EOF
317+
),
318+
$result->getBody()->getContents()
319+
);
320+
}
321+
271322
public function testNestedAndMixedValues(): void
272323
{
273324
$dataResponse = $this->createResponse([
@@ -291,7 +342,7 @@ public function testNestedAndMixedValues(): void
291342
$this->xml(
292343
<<<EOF
293344
<response>
294-
<dummy-class key="0">
345+
<dummy-class>
295346
<string>$object1->string</string>
296347
<int>$object1->int</int>
297348
<float>$object1->float</float>
@@ -302,25 +353,25 @@ public function testNestedAndMixedValues(): void
302353
<float>$object2->float</float>
303354
<array>
304355
<foo>bar</foo>
305-
<item key="0">1.1</item>
306-
<dummy-class key="1">
356+
<item>1.1</item>
357+
<dummy-class>
307358
<string>$object3->string</string>
308359
<int>$object3->int</int>
309360
<float>$object3->float</float>
310361
<array>
311-
<item key="0">true</item>
312-
<item key="1">false</item>
362+
<item>true</item>
363+
<item>false</item>
313364
</array>
314365
</dummy-class>
315366
</array>
316367
</dummy-class>
317368
</array>
318369
</dummy-class>
319-
<item key="1">true</item>
370+
<item>true</item>
320371
<foo>bar</foo>
321-
<item key="2">false</item>
322-
<item key="3">11</item>
323-
<item key="4">baz</item>
372+
<item>false</item>
373+
<item>11</item>
374+
<item>baz</item>
324375
</response>
325376
EOF
326377
),
@@ -342,7 +393,7 @@ public function testItemTagWhenNameIsEmptyOrInvalid(): void
342393
$this->xml(
343394
<<<EOF
344395
<response>
345-
<item key="0">test</item>
396+
<item>test</item>
346397
<validName>test</validName>
347398
<item>test</item>
348399
</response>
@@ -360,30 +411,37 @@ private function createResponse($data): DataResponse
360411
private function xml(string $data, string $version = '1.0', string $encoding = 'UTF-8'): string
361412
{
362413
$startLine = sprintf('<?xml version="%s" encoding="%s"?>', $version, $encoding);
363-
return $startLine . "\n" . preg_replace('/(?!item)\s(?!key)/', '', $data) . "\n";
414+
return $startLine . "\n" . preg_replace('/(?!item)\s(?!attribute)/', '', $data) . "\n";
364415
}
365416

366-
private function createDummyObject(string $string, int $int, float $float, array $array): object
417+
private function createDummyObject(string $string, int $int, float $float, array $array, array $attrs = []): object
367418
{
368-
return new class($string, $int, $float, $array) implements XmlDataInterface {
419+
return new class($string, $int, $float, $array, $attrs) implements XmlDataInterface {
369420
public string $string;
370421
public int $int;
371422
public float $float;
372423
public array $array;
424+
public array $attrs;
373425

374-
public function __construct(string $string, int $int, float $float, array $array)
426+
public function __construct(string $string, int $int, float $float, array $array, array $attrs = [])
375427
{
376428
$this->string = $string;
377429
$this->int = $int;
378430
$this->float = $float;
379431
$this->array = $array;
432+
$this->attrs = $attrs;
380433
}
381434

382435
public function xmlTagName(): string
383436
{
384437
return 'dummy-class';
385438
}
386439

440+
public function xmlTagAttributes(): array
441+
{
442+
return $this->attrs;
443+
}
444+
387445
public function xmlData(): array
388446
{
389447
return [

0 commit comments

Comments
 (0)