1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace POData\Writers\Json; |
4
|
|
|
|
5
|
|
|
use POData\Common\MimeTypes; |
6
|
|
|
use POData\Common\ODataConstants; |
7
|
|
|
use POData\Common\ODataException; |
8
|
|
|
use POData\Common\Version; |
9
|
|
|
use POData\ObjectModel\ODataBagContent; |
10
|
|
|
use POData\ObjectModel\ODataEntry; |
11
|
|
|
use POData\ObjectModel\ODataFeed; |
12
|
|
|
use POData\ObjectModel\ODataLink; |
13
|
|
|
use POData\ObjectModel\ODataProperty; |
14
|
|
|
use POData\ObjectModel\ODataPropertyContent; |
15
|
|
|
use POData\ObjectModel\ODataURL; |
16
|
|
|
use POData\ObjectModel\ODataURLCollection; |
17
|
|
|
use POData\Providers\ProvidersWrapper; |
18
|
|
|
use POData\Writers\IODataWriter; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Class JsonODataV1Writer is a writer for the json format in OData V1. |
22
|
|
|
*/ |
23
|
|
|
class JsonODataV1Writer implements IODataWriter |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* Json output writer. |
27
|
|
|
*/ |
28
|
|
|
protected $_writer; |
29
|
|
|
|
30
|
|
|
protected $urlKey = ODataConstants::JSON_URI_STRING; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Constructs and initializes the Json output writer. |
34
|
|
|
*/ |
35
|
|
|
public function __construct() |
36
|
|
|
{ |
37
|
|
|
$this->_writer = new JsonWriter(''); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Determines if the given writer is capable of writing the response or not. |
42
|
|
|
* |
43
|
|
|
* @param Version $responseVersion the OData version of the response |
44
|
|
|
* @param string $contentType the Content Type of the response |
45
|
|
|
* |
46
|
|
|
* @return bool true if the writer can handle the response, false otherwise |
47
|
|
|
*/ |
48
|
|
View Code Duplication |
public function canHandle(Version $responseVersion, $contentType) |
|
|
|
|
49
|
|
|
{ |
50
|
|
|
if ($responseVersion != Version::v1()) { |
51
|
|
|
return false; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
$parts = explode(';', $contentType); |
55
|
|
|
|
56
|
|
|
return in_array(MimeTypes::MIME_APPLICATION_JSON, $parts); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Write the given OData model in a specific response format. |
61
|
|
|
* |
62
|
|
|
* @param ODataURL|ODataURLCollection|ODataPropertyContent|ODataFeed|ODataEntry $model Object of requested content |
63
|
|
|
* |
64
|
|
|
* @return JsonODataV1Writer |
65
|
|
|
*/ |
66
|
|
View Code Duplication |
public function write($model) |
|
|
|
|
67
|
|
|
{ |
68
|
|
|
// { "d" : |
|
|
|
|
69
|
|
|
$this->_writer |
70
|
|
|
->startObjectScope() |
71
|
|
|
->writeName('d'); |
72
|
|
|
|
73
|
|
|
if ($model instanceof ODataURL) { |
74
|
|
|
$this->_writer->startObjectScope(); |
75
|
|
|
$this->writeURL($model); |
76
|
|
|
} elseif ($model instanceof ODataURLCollection) { |
77
|
|
|
$this->_writer->startArrayScope(); |
78
|
|
|
$this->writeURLCollection($model); |
79
|
|
|
} elseif ($model instanceof ODataPropertyContent) { |
80
|
|
|
$this->_writer->startObjectScope(); |
81
|
|
|
$this->writeProperties($model); |
82
|
|
|
} elseif ($model instanceof ODataFeed) { |
83
|
|
|
$this->_writer->startArrayScope(); |
84
|
|
|
$this->writeFeed($model); |
85
|
|
|
} elseif ($model instanceof ODataEntry) { |
86
|
|
|
$this->_writer->startObjectScope(); |
87
|
|
|
$this->writeEntry($model); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
$this->_writer->endScope(); |
91
|
|
|
$this->_writer->endScope(); |
92
|
|
|
|
93
|
|
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param ODataURL $url the url to write |
98
|
|
|
* |
99
|
|
|
* @return JsonODataV1Writer |
100
|
|
|
*/ |
101
|
|
|
public function writeUrl(ODataURL $url) |
102
|
|
|
{ |
103
|
|
|
$this->_writer |
104
|
|
|
->writeName($this->urlKey) |
105
|
|
|
->writeValue($url->url); |
106
|
|
|
|
107
|
|
|
return $this; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* begin write OData links. |
112
|
|
|
* |
113
|
|
|
* @param ODataURLCollection $urls url collection to write |
114
|
|
|
* |
115
|
|
|
* @return JsonODataV1Writer |
116
|
|
|
*/ |
117
|
|
|
public function writeUrlCollection(ODataURLCollection $urls) |
118
|
|
|
{ |
119
|
|
|
foreach ($urls->urls as $url) { |
120
|
|
|
$this->_writer->startObjectScope(); |
121
|
|
|
$this->writeUrl($url); |
122
|
|
|
$this->_writer->endScope(); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
return $this; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Start writing a feed. |
130
|
|
|
* |
131
|
|
|
* @param ODataFeed $feed Feed to write |
132
|
|
|
* |
133
|
|
|
* @return JsonODataV1Writer |
134
|
|
|
*/ |
135
|
|
|
protected function writeFeed(ODataFeed $feed) |
136
|
|
|
{ |
137
|
|
|
foreach ($feed->entries as $entry) { |
138
|
|
|
$this->_writer->startObjectScope(); |
139
|
|
|
$this->writeEntry($entry); |
140
|
|
|
$this->_writer->endScope(); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
return $this; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @param ODataEntry $entry Entry to write |
148
|
|
|
* |
149
|
|
|
* @return JsonODataV1Writer |
150
|
|
|
*/ |
151
|
|
|
protected function writeEntry(ODataEntry $entry) |
152
|
|
|
{ |
153
|
|
|
$this->writeEntryMetadata($entry); |
154
|
|
|
foreach ($entry->links as $link) { |
155
|
|
|
$this->writeLink($link); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$this->writeProperties($entry->propertyContent); |
159
|
|
|
|
160
|
|
|
return $this; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Write metadata information for the entry. |
165
|
|
|
* |
166
|
|
|
* @param ODataEntry $entry Entry to write metadata for |
167
|
|
|
* |
168
|
|
|
* @return JsonODataV1Writer |
169
|
|
|
*/ |
170
|
|
|
protected function writeEntryMetadata(ODataEntry $entry) |
171
|
|
|
{ |
172
|
|
|
// __metadata : { uri: "Uri", type: "Type" [Media Link Properties] } |
173
|
|
|
if ($entry->id != null || $entry->type != null || $entry->eTag != null) { |
174
|
|
|
// "__metadata" |
175
|
|
|
$this->_writer |
176
|
|
|
->writeName(ODataConstants::JSON_METADATA_STRING) |
177
|
|
|
->startObjectScope(); |
178
|
|
|
|
179
|
|
|
// Write uri value only for entity types |
180
|
|
|
if ($entry->id != null) { |
181
|
|
|
$this->_writer |
182
|
|
|
->writeName($this->urlKey) |
183
|
|
|
->writeValue($entry->id); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
// Write the etag property, if the entry has etag properties. |
187
|
|
|
if ($entry->eTag != null) { |
188
|
|
|
$this->_writer |
189
|
|
|
->writeName(ODataConstants::JSON_ETAG_STRING) |
190
|
|
|
->writeValue($entry->eTag); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// Write the type property, if the entry has type properties. |
194
|
|
|
if ($entry->type != null) { |
195
|
|
|
$this->_writer |
196
|
|
|
->writeName(ODataConstants::JSON_TYPE_STRING) |
197
|
|
|
->writeValue($entry->type); |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// Media links. |
202
|
|
|
if ($entry->isMediaLinkEntry) { |
203
|
|
|
if ($entry->mediaLink != null) { |
204
|
|
|
$this->_writer |
205
|
|
|
->writeName(ODataConstants::JSON_EDITMEDIA_STRING) |
206
|
|
|
->writeValue($entry->mediaLink->editLink) |
207
|
|
|
|
208
|
|
|
->writeName(ODataConstants::JSON_MEDIASRC_STRING) |
209
|
|
|
->writeValue($entry->mediaLink->srcLink) |
210
|
|
|
|
211
|
|
|
->writeName(ODataConstants::JSON_CONTENTTYPE_STRING) |
212
|
|
|
->writeValue($entry->mediaLink->contentType); |
213
|
|
|
|
214
|
|
|
if ($entry->mediaLink->eTag != null) { |
215
|
|
|
$this->_writer |
216
|
|
|
->writeName(ODataConstants::JSON_MEDIAETAG_STRING) |
217
|
|
|
->writeValue($entry->mediaLink->eTag); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$this->_writer->endScope(); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
// writing named resource streams |
224
|
|
|
foreach ($entry->mediaLinks as $mediaLink) { |
225
|
|
|
$this->_writer |
226
|
|
|
->writeName($mediaLink->name) |
227
|
|
|
->startObjectScope() |
228
|
|
|
|
229
|
|
|
->writeName(ODataConstants::JSON_MEDIASRC_STRING) |
230
|
|
|
->writeValue($mediaLink->srcLink) |
231
|
|
|
|
232
|
|
|
->writeName(ODataConstants::JSON_CONTENTTYPE_STRING) |
233
|
|
|
->writeValue($mediaLink->contentType); |
234
|
|
|
|
235
|
|
|
if ($mediaLink->eTag != null) { |
236
|
|
|
$this->_writer |
237
|
|
|
->writeName(ODataConstants::JSON_MEDIAETAG_STRING) |
238
|
|
|
->writeValue($mediaLink->eTag); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
$this->_writer->endScope(); |
242
|
|
|
} |
243
|
|
|
} else { |
244
|
|
|
$this->_writer->endScope(); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
return $this; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* @param ODataLink $link Link to write |
252
|
|
|
* |
253
|
|
|
* @return JsonODataV1Writer |
254
|
|
|
*/ |
255
|
|
|
protected function writeLink(ODataLink $link) |
256
|
|
|
{ |
257
|
|
|
|
258
|
|
|
// "<linkname>" : |
259
|
|
|
$this->_writer->writeName($link->title); |
260
|
|
|
|
261
|
|
|
if ($link->isExpanded) { |
262
|
|
|
if (is_null($link->expandedResult)) { |
263
|
|
|
$this->_writer->writeValue('null'); |
264
|
|
|
} else { |
265
|
|
|
$this->writeExpandedLink($link); |
266
|
|
|
} |
267
|
|
|
} else { |
268
|
|
|
$this->_writer |
269
|
|
|
->startObjectScope() |
270
|
|
|
->writeName(ODataConstants::JSON_DEFERRED_STRING) |
271
|
|
|
->startObjectScope() |
272
|
|
|
->writeName($this->urlKey) |
273
|
|
|
->writeValue($link->url) |
274
|
|
|
->endScope() |
275
|
|
|
->endScope(); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
return $this; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
View Code Duplication |
protected function writeExpandedLink(ODataLink $link) |
|
|
|
|
282
|
|
|
{ |
283
|
|
|
if ($link->isCollection) { |
284
|
|
|
$this->_writer->startArrayScope(); |
285
|
|
|
$this->writeFeed($link->expandedResult); |
|
|
|
|
286
|
|
|
} else { |
287
|
|
|
$this->_writer->startObjectScope(); |
288
|
|
|
$this->writeEntry($link->expandedResult); |
|
|
|
|
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
$this->_writer->endScope(); |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Write the given collection of properties. |
296
|
|
|
* (properties of an entity or complex type). |
297
|
|
|
* |
298
|
|
|
* @param ODataPropertyContent $properties Collection of properties |
299
|
|
|
* |
300
|
|
|
* @return JsonODataV1Writer |
301
|
|
|
*/ |
302
|
|
|
protected function writeProperties(ODataPropertyContent $properties) |
303
|
|
|
{ |
304
|
|
|
foreach ($properties->properties as $property) { |
305
|
|
|
$this->writePropertyMeta($property); |
306
|
|
|
$this->_writer->writeName($property->name); |
307
|
|
|
|
308
|
|
|
if ($property->value == null) { |
309
|
|
|
$this->_writer->writeValue('null'); |
310
|
|
|
} elseif ($property->value instanceof ODataPropertyContent) { |
311
|
|
|
$this->writeComplexProperty($property); |
312
|
|
|
} elseif ($property->value instanceof ODataBagContent) { |
313
|
|
|
$this->writeBagContent($property->value); |
314
|
|
|
} else { |
315
|
|
|
$this->_writer->writeValue($property->value, $property->typeName); |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
return $this; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
protected function writePropertyMeta(ODataProperty $property) |
323
|
|
|
{ |
324
|
|
|
return $this; //does nothing in v1 or v2, json light outputs stuff |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Begin write complex property. |
329
|
|
|
* |
330
|
|
|
* @param ODataProperty $property property to write |
331
|
|
|
* |
332
|
|
|
* @return JsonODataV1Writer |
333
|
|
|
*/ |
334
|
|
|
protected function writeComplexProperty(ODataProperty $property) |
335
|
|
|
{ |
336
|
|
|
$this->_writer |
337
|
|
|
// { |
338
|
|
|
->startObjectScope() |
339
|
|
|
|
340
|
|
|
// __metadata : { Type : "typename" } |
|
|
|
|
341
|
|
|
->writeName(ODataConstants::JSON_METADATA_STRING) |
342
|
|
|
->startObjectScope() |
343
|
|
|
->writeName(ODataConstants::JSON_TYPE_STRING) |
344
|
|
|
->writeValue($property->typeName) |
345
|
|
|
->endScope(); |
346
|
|
|
|
347
|
|
|
$this->writeProperties($property->value); |
|
|
|
|
348
|
|
|
|
349
|
|
|
$this->_writer->endScope(); |
350
|
|
|
|
351
|
|
|
return $this; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* Begin an item in a collection. |
356
|
|
|
* |
357
|
|
|
* @param ODataBagContent $bag bag property to write |
358
|
|
|
* |
359
|
|
|
* @return JsonODataV1Writer |
360
|
|
|
*/ |
361
|
|
|
protected function writeBagContent(ODataBagContent $bag) |
362
|
|
|
{ |
363
|
|
|
$this->_writer |
364
|
|
|
->startObjectScope() // { |
365
|
|
|
->writeName(ODataConstants::JSON_METADATA_STRING) //__metadata : { Type : "typename" } |
|
|
|
|
366
|
|
|
->startObjectScope() |
367
|
|
|
|
368
|
|
|
->writeName(ODataConstants::JSON_TYPE_STRING) |
369
|
|
|
->writeValue($bag->type) |
370
|
|
|
->endScope() // } |
371
|
|
|
->writeName(ODataConstants::JSON_RESULT_NAME) // "__results": |
372
|
|
|
->startArrayScope(); // [ |
373
|
|
|
|
374
|
|
View Code Duplication |
foreach ($bag->propertyContents as $content) { |
|
|
|
|
375
|
|
|
if ($content instanceof ODataPropertyContent) { |
376
|
|
|
$this->_writer->startObjectScope(); |
377
|
|
|
$this->writeProperties($content); |
378
|
|
|
$this->_writer->endScope(); |
379
|
|
|
} else { |
380
|
|
|
// retrieving the collection datatype in order |
381
|
|
|
//to write in json specific format, with in chords or not |
382
|
|
|
preg_match('#\((.*?)\)#', $bag->type, $type); |
383
|
|
|
$this->_writer->writeValue($content, $type[1]); |
384
|
|
|
} |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
$this->_writer |
388
|
|
|
->endScope() // ] |
389
|
|
|
->endScope(); // } |
390
|
|
|
return $this; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* serialize exception. |
395
|
|
|
* |
396
|
|
|
* @param ODataException $exception Exception to serialize |
397
|
|
|
* @param bool $serializeInnerException if set to true |
398
|
|
|
* |
399
|
|
|
* serialize the inner exception if $exception is an ODataException |
400
|
|
|
* |
401
|
|
|
* @return string |
402
|
|
|
*/ |
403
|
|
|
public static function serializeException(ODataException $exception, $serializeInnerException) |
|
|
|
|
404
|
|
|
{ |
405
|
|
|
$writer = new JsonWriter(''); |
406
|
|
|
// Wrapper for error. |
407
|
|
|
$writer |
408
|
|
|
->startObjectScope() |
409
|
|
|
->writeName(ODataConstants::JSON_ERROR) // "error" |
410
|
|
|
->startObjectScope(); |
411
|
|
|
|
412
|
|
|
// "code" |
413
|
|
|
if ($exception->getCode() != null) { |
414
|
|
|
$writer |
415
|
|
|
->writeName(ODataConstants::JSON_ERROR_CODE) |
416
|
|
|
->writeValue($exception->getCode()); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
// "message" |
420
|
|
|
$writer |
421
|
|
|
->writeName(ODataConstants::JSON_ERROR_MESSAGE) |
422
|
|
|
->startObjectScope() |
423
|
|
|
->writeName(ODataConstants::XML_LANG_ATTRIBUTE_NAME) // "lang" |
424
|
|
|
->writeValue('en-US') |
425
|
|
|
->writeName(ODataConstants::JSON_ERROR_VALUE) |
426
|
|
|
->writeValue($exception->getMessage()) |
427
|
|
|
|
428
|
|
|
->endScope() |
429
|
|
|
->endScope() |
430
|
|
|
->endScope(); |
431
|
|
|
|
432
|
|
|
return $writer->getJsonOutput(); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Get the Json final output. |
437
|
|
|
* |
438
|
|
|
* @return string |
439
|
|
|
*/ |
440
|
|
|
public function getOutput() |
441
|
|
|
{ |
442
|
|
|
return $this->_writer->getJsonOutput(); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* @param ProvidersWrapper $providers |
447
|
|
|
* |
448
|
|
|
* @return IODataWriter |
449
|
|
|
*/ |
450
|
|
|
public function writeServiceDocument(ProvidersWrapper $providers) |
451
|
|
|
{ |
452
|
|
|
$writer = $this->_writer; |
453
|
|
|
$writer |
454
|
|
|
->startObjectScope() // { |
455
|
|
|
->writeName('d') // "d" : |
456
|
|
|
->startObjectScope() // { |
457
|
|
|
->writeName(ODataConstants::ENTITY_SET) // "EntitySets" |
458
|
|
|
->startArrayScope() // [ |
459
|
|
|
; |
460
|
|
|
|
461
|
|
|
foreach ($providers->getResourceSets() as $resourceSetWrapper) { |
462
|
|
|
$writer->writeValue($resourceSetWrapper->getName()); |
463
|
|
|
} |
464
|
|
|
foreach ($providers->getSingletons() as $singleton) { |
465
|
|
|
$writer->writeValue($singleton->getName()); |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
$writer |
469
|
|
|
->endScope() // ] |
470
|
|
|
->endScope() // } |
471
|
|
|
->endScope() // } |
472
|
|
|
; |
473
|
|
|
|
474
|
|
|
return $this; |
475
|
|
|
} |
476
|
|
|
} |
477
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.