BaseSerializationTest.php 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520
  1. <?php
  2. namespace JMS\Serializer\Tests\Serializer;
  3. use Doctrine\Common\Annotations\AnnotationReader;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use JMS\Serializer\Accessor\DefaultAccessorStrategy;
  6. use JMS\Serializer\Accessor\ExpressionAccessorStrategy;
  7. use JMS\Serializer\Construction\UnserializeObjectConstructor;
  8. use JMS\Serializer\Context;
  9. use JMS\Serializer\DeserializationContext;
  10. use JMS\Serializer\EventDispatcher\EventDispatcher;
  11. use JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber;
  12. use JMS\Serializer\Exclusion\DepthExclusionStrategy;
  13. use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
  14. use JMS\Serializer\Expression\ExpressionEvaluator;
  15. use JMS\Serializer\GraphNavigator;
  16. use JMS\Serializer\Handler\ArrayCollectionHandler;
  17. use JMS\Serializer\Handler\ConstraintViolationHandler;
  18. use JMS\Serializer\Handler\DateHandler;
  19. use JMS\Serializer\Handler\FormErrorHandler;
  20. use JMS\Serializer\Handler\HandlerRegistry;
  21. use JMS\Serializer\Handler\PhpCollectionHandler;
  22. use JMS\Serializer\Handler\StdClassHandler;
  23. use JMS\Serializer\JsonDeserializationVisitor;
  24. use JMS\Serializer\JsonSerializationVisitor;
  25. use JMS\Serializer\Metadata\Driver\AnnotationDriver;
  26. use JMS\Serializer\Metadata\Driver\YamlDriver;
  27. use JMS\Serializer\Naming\CamelCaseNamingStrategy;
  28. use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;
  29. use JMS\Serializer\SerializationContext;
  30. use JMS\Serializer\Serializer;
  31. use JMS\Serializer\Tests\Fixtures\AccessorOrderChild;
  32. use JMS\Serializer\Tests\Fixtures\AccessorOrderMethod;
  33. use JMS\Serializer\Tests\Fixtures\AccessorOrderParent;
  34. use JMS\Serializer\Tests\Fixtures\Article;
  35. use JMS\Serializer\Tests\Fixtures\Author;
  36. use JMS\Serializer\Tests\Fixtures\AuthorExpressionAccess;
  37. use JMS\Serializer\Tests\Fixtures\AuthorList;
  38. use JMS\Serializer\Tests\Fixtures\AuthorReadOnly;
  39. use JMS\Serializer\Tests\Fixtures\AuthorReadOnlyPerClass;
  40. use JMS\Serializer\Tests\Fixtures\BlogPost;
  41. use JMS\Serializer\Tests\Fixtures\CircularReferenceParent;
  42. use JMS\Serializer\Tests\Fixtures\Comment;
  43. use JMS\Serializer\Tests\Fixtures\CurrencyAwareOrder;
  44. use JMS\Serializer\Tests\Fixtures\CurrencyAwarePrice;
  45. use JMS\Serializer\Tests\Fixtures\CustomDeserializationObject;
  46. use JMS\Serializer\Tests\Fixtures\DateTimeArraysObject;
  47. use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
  48. use JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost;
  49. use JMS\Serializer\Tests\Fixtures\Discriminator\Moped;
  50. use JMS\Serializer\Tests\Fixtures\Discriminator\Post;
  51. use JMS\Serializer\Tests\Fixtures\Garage;
  52. use JMS\Serializer\Tests\Fixtures\GetSetObject;
  53. use JMS\Serializer\Tests\Fixtures\GroupsObject;
  54. use JMS\Serializer\Tests\Fixtures\GroupsUser;
  55. use JMS\Serializer\Tests\Fixtures\IndexedCommentsBlogPost;
  56. use JMS\Serializer\Tests\Fixtures\InitializedBlogPostConstructor;
  57. use JMS\Serializer\Tests\Fixtures\InitializedObjectConstructor;
  58. use JMS\Serializer\Tests\Fixtures\InlineChild;
  59. use JMS\Serializer\Tests\Fixtures\InlineChildEmpty;
  60. use JMS\Serializer\Tests\Fixtures\InlineChildWithGroups;
  61. use JMS\Serializer\Tests\Fixtures\InlineParent;
  62. use JMS\Serializer\Tests\Fixtures\Input;
  63. use JMS\Serializer\Tests\Fixtures\InvalidGroupsObject;
  64. use JMS\Serializer\Tests\Fixtures\Log;
  65. use JMS\Serializer\Tests\Fixtures\MaxDepth\Gh236Foo;
  66. use JMS\Serializer\Tests\Fixtures\NamedDateTimeArraysObject;
  67. use JMS\Serializer\Tests\Fixtures\NamedDateTimeImmutableArraysObject;
  68. use JMS\Serializer\Tests\Fixtures\Node;
  69. use JMS\Serializer\Tests\Fixtures\ObjectUsingTypeCasting;
  70. use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyHash;
  71. use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyNullableAndEmptyArrays;
  72. use JMS\Serializer\Tests\Fixtures\ObjectWithIntListAndIntMap;
  73. use JMS\Serializer\Tests\Fixtures\ObjectWithLifecycleCallbacks;
  74. use JMS\Serializer\Tests\Fixtures\ObjectWithNullProperty;
  75. use JMS\Serializer\Tests\Fixtures\ObjectWithToString;
  76. use JMS\Serializer\Tests\Fixtures\ObjectWithVersionedVirtualProperties;
  77. use JMS\Serializer\Tests\Fixtures\ObjectWithVirtualProperties;
  78. use JMS\Serializer\Tests\Fixtures\Order;
  79. use JMS\Serializer\Tests\Fixtures\ParentDoNotSkipWithEmptyChild;
  80. use JMS\Serializer\Tests\Fixtures\ParentSkipWithEmptyChild;
  81. use JMS\Serializer\Tests\Fixtures\PersonSecret;
  82. use JMS\Serializer\Tests\Fixtures\PersonSecretMore;
  83. use JMS\Serializer\Tests\Fixtures\PersonSecretMoreVirtual;
  84. use JMS\Serializer\Tests\Fixtures\PersonSecretVirtual;
  85. use JMS\Serializer\Tests\Fixtures\Price;
  86. use JMS\Serializer\Tests\Fixtures\Publisher;
  87. use JMS\Serializer\Tests\Fixtures\SimpleInternalObject;
  88. use JMS\Serializer\Tests\Fixtures\SimpleObject;
  89. use JMS\Serializer\Tests\Fixtures\SimpleObjectProxy;
  90. use JMS\Serializer\Tests\Fixtures\SimpleObjectWithStaticProp;
  91. use JMS\Serializer\Tests\Fixtures\Tag;
  92. use JMS\Serializer\Tests\Fixtures\Timestamp;
  93. use JMS\Serializer\Tests\Fixtures\Tree;
  94. use JMS\Serializer\Tests\Fixtures\VehicleInterfaceGarage;
  95. use JMS\Serializer\VisitorInterface;
  96. use JMS\Serializer\XmlDeserializationVisitor;
  97. use JMS\Serializer\XmlSerializationVisitor;
  98. use JMS\Serializer\YamlSerializationVisitor;
  99. use Metadata\Driver\FileLocator;
  100. use Metadata\MetadataFactory;
  101. use PhpCollection\Map;
  102. use PhpCollection\Sequence;
  103. use Symfony\Component\ExpressionLanguage\ExpressionFunction;
  104. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  105. use Symfony\Component\Form\Form;
  106. use Symfony\Component\Form\FormError;
  107. use Symfony\Component\Form\FormFactoryBuilder;
  108. use Symfony\Component\Translation\IdentityTranslator;
  109. use Symfony\Component\Translation\MessageSelector;
  110. use Symfony\Component\Validator\ConstraintViolation;
  111. use Symfony\Component\Validator\ConstraintViolationList;
  112. abstract class BaseSerializationTest extends \PHPUnit_Framework_TestCase
  113. {
  114. protected $factory;
  115. /**
  116. * @var EventDispatcher
  117. */
  118. protected $dispatcher;
  119. /** @var Serializer */
  120. protected $serializer;
  121. protected $handlerRegistry;
  122. protected $serializationVisitors;
  123. protected $deserializationVisitors;
  124. protected $objectConstructor;
  125. public function testSerializeNullArray()
  126. {
  127. $arr = array('foo' => 'bar', 'baz' => null, null);
  128. $this->assertEquals(
  129. $this->getContent('nullable'),
  130. $this->serializer->serialize($arr, $this->getFormat(), SerializationContext::create()->setSerializeNull(true))
  131. );
  132. }
  133. public function testSerializeNullArrayExcludingNulls()
  134. {
  135. $arr = array('foo' => 'bar', 'baz' => null, null);
  136. $this->assertEquals(
  137. $this->getContent('nullable_skip'),
  138. $this->serializer->serialize($arr, $this->getFormat(), SerializationContext::create()->setSerializeNull(false))
  139. );
  140. }
  141. public function testObjectUsingTypeCasting()
  142. {
  143. $typeAliasing = new ObjectUsingTypeCasting();
  144. $typeAliasing->asString = new ObjectWithToString("8");
  145. $this->assertEquals(
  146. $this->getContent('type_casting'),
  147. $this->serialize($typeAliasing)
  148. );
  149. }
  150. public function testSerializeNullObject()
  151. {
  152. $obj = new ObjectWithNullProperty('foo', 'bar');
  153. $this->assertEquals(
  154. $this->getContent('simple_object_nullable'),
  155. $this->serializer->serialize($obj, $this->getFormat(), SerializationContext::create()->setSerializeNull(true))
  156. );
  157. }
  158. public function testDeserializeNullObject()
  159. {
  160. if (!$this->hasDeserializer()) {
  161. $this->markTestSkipped(sprintf('No deserializer available for format `%s`', $this->getFormat()));
  162. }
  163. $obj = new ObjectWithNullProperty('foo', 'bar');
  164. /** @var ObjectWithNullProperty $dObj */
  165. $dObj = $this->serializer->deserialize(
  166. $this->getContent('simple_object_nullable'),
  167. ObjectWithNullProperty::class,
  168. $this->getFormat()
  169. );
  170. $this->assertEquals($obj, $dObj);
  171. $this->assertNull($dObj->getNullProperty());
  172. }
  173. /**
  174. * @dataProvider getTypes
  175. */
  176. public function testNull($type)
  177. {
  178. $this->assertEquals($this->getContent('null'), $this->serialize(null), $type);
  179. if ($this->hasDeserializer()) {
  180. $this->assertEquals(null, $this->deserialize($this->getContent('null'), $type));
  181. }
  182. }
  183. public function getTypes()
  184. {
  185. return array(
  186. array('NULL'),
  187. array('integer'),
  188. array('double'),
  189. array('float'),
  190. array('string'),
  191. array('DateTime'),
  192. );
  193. }
  194. public function testString()
  195. {
  196. $this->assertEquals($this->getContent('string'), $this->serialize('foo'));
  197. if ($this->hasDeserializer()) {
  198. $this->assertEquals('foo', $this->deserialize($this->getContent('string'), 'string'));
  199. }
  200. }
  201. /**
  202. * @expectedException \JMS\Serializer\Exception\ExpressionLanguageRequiredException
  203. * @expectedExceptionMessage To use conditional exclude/expose in JMS\Serializer\Tests\Fixtures\PersonSecret you must configure the expression language.
  204. */
  205. public function testExpressionExclusionNotConfigured()
  206. {
  207. $person = new PersonSecret();
  208. $person->gender = 'f';
  209. $person->name = 'mike';
  210. $this->serialize($person);
  211. }
  212. public function testExpressionExclusionConfiguredWithDisjunctStrategy()
  213. {
  214. $person = new PersonSecret();
  215. $person->gender = 'f';
  216. $person->name = 'mike';
  217. $language = new ExpressionLanguage();
  218. $language->addFunction(new ExpressionFunction('show_data', function () {
  219. return "true";
  220. }, function () {
  221. return true;
  222. }));
  223. $serializer = new Serializer($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher, null, new ExpressionEvaluator($language));
  224. $this->assertEquals($this->getContent('person_secret_hide'), $serializer->serialize($person, $this->getFormat()));
  225. }
  226. public function expressionFunctionProvider()
  227. {
  228. $person = new PersonSecret();
  229. $person->gender = 'f';
  230. $person->name = 'mike';
  231. $personMoreSecret = new PersonSecretMore();
  232. $personMoreSecret->gender = 'f';
  233. $personMoreSecret->name = 'mike';
  234. $personVirtual = new PersonSecretVirtual();
  235. $personVirtual->gender = 'f';
  236. $personVirtual->name = 'mike';
  237. $personMoreSecretVirtual = new PersonSecretMoreVirtual();
  238. $personMoreSecretVirtual->gender = 'f';
  239. $personMoreSecretVirtual->name = 'mike';
  240. $showGender = new ExpressionFunction('show_data', function () {
  241. return "true";
  242. }, function () {
  243. return true;
  244. });
  245. $hideGender = new ExpressionFunction('show_data', function () {
  246. return "false";
  247. }, function () {
  248. return false;
  249. });
  250. return [
  251. [
  252. $person,
  253. $showGender,
  254. 'person_secret_hide'
  255. ],
  256. [
  257. $person,
  258. $hideGender,
  259. 'person_secret_show'
  260. ],
  261. [
  262. $personMoreSecret,
  263. $showGender,
  264. 'person_secret_show'
  265. ],
  266. [
  267. $personMoreSecret,
  268. $hideGender,
  269. 'person_secret_hide'
  270. ],
  271. [
  272. $personVirtual,
  273. $showGender,
  274. 'person_secret_hide'
  275. ],
  276. [
  277. $personVirtual,
  278. $hideGender,
  279. 'person_secret_show'
  280. ],
  281. [
  282. $personMoreSecretVirtual,
  283. $showGender,
  284. 'person_secret_show'
  285. ],
  286. [
  287. $personMoreSecretVirtual,
  288. $hideGender,
  289. 'person_secret_hide'
  290. ]
  291. ];
  292. }
  293. /**
  294. * @dataProvider expressionFunctionProvider
  295. * @param PersonSecret|PersonSecretMore $person
  296. * @param ExpressionFunction $function
  297. * @param $json
  298. */
  299. public function testExpressionExclusion($person, ExpressionFunction $function, $json)
  300. {
  301. $language = new ExpressionLanguage();
  302. $language->addFunction($function);
  303. $serializer = new Serializer($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher, null, new ExpressionEvaluator($language));
  304. $this->assertEquals($this->getContent($json), $serializer->serialize($person, $this->getFormat()));
  305. }
  306. /**
  307. * @dataProvider getBooleans
  308. */
  309. public function testBooleans($strBoolean, $boolean)
  310. {
  311. $this->assertEquals($this->getContent('boolean_' . $strBoolean), $this->serialize($boolean));
  312. if ($this->hasDeserializer()) {
  313. $this->assertSame($boolean, $this->deserialize($this->getContent('boolean_' . $strBoolean), 'boolean'));
  314. }
  315. }
  316. public function getBooleans()
  317. {
  318. return array(array('true', true), array('false', false));
  319. }
  320. /**
  321. * @dataProvider getNumerics
  322. */
  323. public function testNumerics($key, $value, $type)
  324. {
  325. $this->assertEquals($this->getContent($key), $this->serialize($value));
  326. if ($this->hasDeserializer()) {
  327. $this->assertEquals($value, $this->deserialize($this->getContent($key), $type));
  328. }
  329. }
  330. public function getNumerics()
  331. {
  332. return array(
  333. array('integer', 1, 'integer'),
  334. array('float', 4.533, 'double'),
  335. array('float', 4.533, 'float'),
  336. array('float_trailing_zero', 1.0, 'double'),
  337. array('float_trailing_zero', 1.0, 'float'),
  338. );
  339. }
  340. public function testSimpleInternalObject()
  341. {
  342. $this->factory = new MetadataFactory(new YamlDriver(new FileLocator([
  343. 'JMS\Serializer\Tests\Fixtures' => __DIR__ .'/metadata/SimpleInternalObject',
  344. '' => __DIR__ .'/metadata/SimpleInternalObject'
  345. ])));
  346. $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
  347. $obj = new SimpleInternalObject('foo', 'bar');
  348. $this->assertEquals($this->getContent('simple_object'), $this->serialize($obj));
  349. if ($this->hasDeserializer()) {
  350. $deserialized = $this->deserialize($this->getContent('simple_object'), get_class($obj));
  351. $this->assertEquals(get_class($obj), get_class($deserialized));
  352. }
  353. }
  354. public function testSimpleObjectStaticProp()
  355. {
  356. $this->assertEquals($this->getContent('simple_object'), $this->serialize($obj = new SimpleObjectWithStaticProp('foo', 'bar')));
  357. if ($this->hasDeserializer()) {
  358. $this->assertEquals($obj, $this->deserialize($this->getContent('simple_object'), get_class($obj)));
  359. }
  360. }
  361. public function testSimpleObject()
  362. {
  363. $this->assertEquals($this->getContent('simple_object'), $this->serialize($obj = new SimpleObject('foo', 'bar')));
  364. if ($this->hasDeserializer()) {
  365. $this->assertEquals($obj, $this->deserialize($this->getContent('simple_object'), get_class($obj)));
  366. }
  367. }
  368. public function testArrayStrings()
  369. {
  370. $data = array('foo', 'bar');
  371. $this->assertEquals($this->getContent('array_strings'), $this->serialize($data));
  372. if ($this->hasDeserializer()) {
  373. $this->assertEquals($data, $this->deserialize($this->getContent('array_strings'), 'array<string>'));
  374. }
  375. }
  376. public function testArrayBooleans()
  377. {
  378. $data = array(true, false);
  379. $this->assertEquals($this->getContent('array_booleans'), $this->serialize($data));
  380. if ($this->hasDeserializer()) {
  381. $this->assertEquals($data, $this->deserialize($this->getContent('array_booleans'), 'array<boolean>'));
  382. }
  383. }
  384. public function testArrayIntegers()
  385. {
  386. $data = array(1, 3, 4);
  387. $this->assertEquals($this->getContent('array_integers'), $this->serialize($data));
  388. if ($this->hasDeserializer()) {
  389. $this->assertEquals($data, $this->deserialize($this->getContent('array_integers'), 'array<integer>'));
  390. }
  391. }
  392. public function testArrayEmpty()
  393. {
  394. if ('xml' === $this->getFormat()) {
  395. $this->markTestSkipped('XML can\'t be tested for empty array');
  396. }
  397. $data = array('array' => []);
  398. $this->assertEquals($this->getContent('array_empty'), $this->serialize($data));
  399. if ($this->hasDeserializer()) {
  400. $this->assertEquals($data, $this->deserialize($this->getContent('array_empty'), 'array'));
  401. }
  402. }
  403. public function testArrayFloats()
  404. {
  405. $data = array(1.34, 3.0, 6.42);
  406. $this->assertEquals($this->getContent('array_floats'), $this->serialize($data));
  407. if ($this->hasDeserializer()) {
  408. $this->assertEquals($data, $this->deserialize($this->getContent('array_floats'), 'array<double>'));
  409. }
  410. }
  411. public function testArrayObjects()
  412. {
  413. $data = array(new SimpleObject('foo', 'bar'), new SimpleObject('baz', 'boo'));
  414. $this->assertEquals($this->getContent('array_objects'), $this->serialize($data));
  415. if ($this->hasDeserializer()) {
  416. $this->assertEquals($data, $this->deserialize($this->getContent('array_objects'), 'array<JMS\Serializer\Tests\Fixtures\SimpleObject>'));
  417. }
  418. }
  419. public function testArrayListAndMapDifference()
  420. {
  421. $arrayData = array(0 => 1, 2 => 2, 3 => 3); // Misses key 1
  422. $data = new ObjectWithIntListAndIntMap($arrayData, $arrayData);
  423. $this->assertEquals($this->getContent('array_list_and_map_difference'), $this->serialize($data));
  424. }
  425. public function testDateTimeArrays()
  426. {
  427. $data = array(
  428. new \DateTime('2047-01-01 12:47:47', new \DateTimeZone('UTC')),
  429. new \DateTime('2016-12-05 00:00:00', new \DateTimeZone('UTC'))
  430. );
  431. $object = new DateTimeArraysObject($data, $data);
  432. $serializedObject = $this->serialize($object);
  433. $this->assertEquals($this->getContent('array_datetimes_object'), $serializedObject);
  434. if ($this->hasDeserializer()) {
  435. /** @var DateTimeArraysObject $deserializedObject */
  436. $deserializedObject = $this->deserialize($this->getContent('array_datetimes_object'), 'Jms\Serializer\Tests\Fixtures\DateTimeArraysObject');
  437. /** deserialized object has a default timezone set depending on user's timezone settings. That's why we manually set the UTC timezone on the DateTime objects. */
  438. foreach ($deserializedObject->getArrayWithDefaultDateTime() as $dateTime) {
  439. $dateTime->setTimezone(new \DateTimeZone('UTC'));
  440. }
  441. foreach ($deserializedObject->getArrayWithFormattedDateTime() as $dateTime) {
  442. $dateTime->setTimezone(new \DateTimeZone('UTC'));
  443. }
  444. $this->assertEquals($object, $deserializedObject);
  445. }
  446. }
  447. public function testNamedDateTimeArrays()
  448. {
  449. $data = array(
  450. new \DateTime('2047-01-01 12:47:47', new \DateTimeZone('UTC')),
  451. new \DateTime('2016-12-05 00:00:00', new \DateTimeZone('UTC'))
  452. );
  453. $object = new NamedDateTimeArraysObject(array('testdate1' => $data[0], 'testdate2' => $data[1]));
  454. $serializedObject = $this->serialize($object);
  455. $this->assertEquals($this->getContent('array_named_datetimes_object'), $serializedObject);
  456. if ($this->hasDeserializer()) {
  457. // skip XML deserialization
  458. if ($this->getFormat() === 'xml') {
  459. return;
  460. }
  461. /** @var NamedDateTimeArraysObject $deserializedObject */
  462. $deserializedObject = $this->deserialize($this->getContent('array_named_datetimes_object'), 'Jms\Serializer\Tests\Fixtures\NamedDateTimeArraysObject');
  463. /** deserialized object has a default timezone set depending on user's timezone settings. That's why we manually set the UTC timezone on the DateTime objects. */
  464. foreach ($deserializedObject->getNamedArrayWithFormattedDate() as $dateTime) {
  465. $dateTime->setTimezone(new \DateTimeZone('UTC'));
  466. }
  467. $this->assertEquals($object, $deserializedObject);
  468. }
  469. }
  470. /**
  471. * @group datetime
  472. */
  473. public function testNamedDateTimeImmutableArrays()
  474. {
  475. $data = array(
  476. new \DateTimeImmutable('2047-01-01 12:47:47', new \DateTimeZone('UTC')),
  477. new \DateTimeImmutable('2016-12-05 00:00:00', new \DateTimeZone('UTC'))
  478. );
  479. $object = new NamedDateTimeImmutableArraysObject(array('testdate1' => $data[0], 'testdate2' => $data[1]));
  480. $serializedObject = $this->serialize($object);
  481. $this->assertEquals($this->getContent('array_named_datetimeimmutables_object'), $serializedObject);
  482. if ($this->hasDeserializer()) {
  483. if ('xml' == $this->getFormat()) {
  484. $this->markTestSkipped("XML deserialization does not support key-val pairs mode");
  485. }
  486. /** @var NamedDateTimeArraysObject $deserializedObject */
  487. $deserializedObject = $this->deserialize($this->getContent('array_named_datetimeimmutables_object'), 'Jms\Serializer\Tests\Fixtures\NamedDateTimeImmutableArraysObject');
  488. /** deserialized object has a default timezone set depending on user's timezone settings. That's why we manually set the UTC timezone on the DateTime objects. */
  489. foreach ($deserializedObject->getNamedArrayWithFormattedDate() as $dateTime) {
  490. $dateTime->setTimezone(new \DateTimeZone('UTC'));
  491. }
  492. $this->assertEquals($object, $deserializedObject);
  493. }
  494. }
  495. public function testArrayMixed()
  496. {
  497. $this->assertEquals($this->getContent('array_mixed'), $this->serialize(array('foo', 1, true, new SimpleObject('foo', 'bar'), array(1, 3, true))));
  498. }
  499. /**
  500. * @dataProvider getDateTime
  501. * @group datetime
  502. */
  503. public function testDateTime($key, $value, $type)
  504. {
  505. $this->assertEquals($this->getContent($key), $this->serialize($value));
  506. if ($this->hasDeserializer()) {
  507. $deserialized = $this->deserialize($this->getContent($key), $type);
  508. $this->assertTrue(is_object($deserialized));
  509. $this->assertEquals(get_class($value), get_class($deserialized));
  510. $this->assertEquals($value->getTimestamp(), $deserialized->getTimestamp());
  511. }
  512. }
  513. public function getDateTime()
  514. {
  515. return array(
  516. array('date_time', new \DateTime('2011-08-30 00:00', new \DateTimeZone('UTC')), 'DateTime'),
  517. );
  518. }
  519. /**
  520. * @dataProvider getDateTimeImmutable
  521. * @group datetime
  522. */
  523. public function testDateTimeImmutable($key, $value, $type)
  524. {
  525. $this->assertEquals($this->getContent($key), $this->serialize($value));
  526. if ($this->hasDeserializer()) {
  527. $deserialized = $this->deserialize($this->getContent($key), $type);
  528. $this->assertTrue(is_object($deserialized));
  529. $this->assertEquals(get_class($value), get_class($deserialized));
  530. $this->assertEquals($value->getTimestamp(), $deserialized->getTimestamp());
  531. }
  532. }
  533. public function getDateTimeImmutable()
  534. {
  535. return array(
  536. array('date_time_immutable', new \DateTimeImmutable('2011-08-30 00:00', new \DateTimeZone('UTC')), 'DateTimeImmutable'),
  537. );
  538. }
  539. public function testTimestamp()
  540. {
  541. $value = new Timestamp(new \DateTime('2016-02-11 00:00:00', new \DateTimeZone('UTC')));
  542. $this->assertEquals($this->getContent('timestamp'), $this->serialize($value));
  543. if ($this->hasDeserializer()) {
  544. $deserialized = $this->deserialize($this->getContent('timestamp'), Timestamp::class);
  545. $this->assertEquals($value, $deserialized);
  546. $this->assertEquals($value->getTimestamp()->getTimestamp(), $deserialized->getTimestamp()->getTimestamp());
  547. $deserialized = $this->deserialize($this->getContent('timestamp_prev'), Timestamp::class);
  548. $this->assertEquals($value, $deserialized);
  549. $this->assertEquals($value->getTimestamp()->getTimestamp(), $deserialized->getTimestamp()->getTimestamp());
  550. }
  551. }
  552. public function testDateInterval()
  553. {
  554. $duration = new \DateInterval('PT45M');
  555. $this->assertEquals($this->getContent('date_interval'), $this->serializer->serialize($duration, $this->getFormat()));
  556. if ($this->hasDeserializer()) {
  557. $deserialized = $this->deserialize($this->getContent('date_interval'), \DateInterval::class);
  558. $this->assertEquals($duration, $deserialized);
  559. $this->assertEquals($duration->i, $deserialized->i);
  560. }
  561. }
  562. public function testBlogPost()
  563. {
  564. $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')), new Publisher('Bar Foo'));
  565. $post->addComment($comment = new Comment($author, 'foo'));
  566. $post->addTag($tag1 = New Tag("tag1"));
  567. $post->addTag($tag2 = New Tag("tag2"));
  568. $this->assertEquals($this->getContent('blog_post'), $this->serialize($post));
  569. if ($this->hasDeserializer()) {
  570. $deserialized = $this->deserialize($this->getContent('blog_post'), get_class($post));
  571. $this->assertEquals('2011-07-30T00:00:00+0000', $this->getField($deserialized, 'createdAt')->format(\DateTime::ISO8601));
  572. $this->assertAttributeEquals('This is a nice title.', 'title', $deserialized);
  573. $this->assertAttributeSame(false, 'published', $deserialized);
  574. $this->assertAttributeSame(false, 'reviewed', $deserialized);
  575. $this->assertAttributeSame('1edf9bf60a32d89afbb85b2be849e3ceed5f5b10', 'etag', $deserialized);
  576. $this->assertAttributeEquals(new ArrayCollection(array($comment)), 'comments', $deserialized);
  577. $this->assertAttributeEquals(new Sequence(array($comment)), 'comments2', $deserialized);
  578. $this->assertAttributeEquals($author, 'author', $deserialized);
  579. $this->assertAttributeEquals(array($tag1, $tag2), 'tag', $deserialized);
  580. }
  581. }
  582. public function testDeserializingNull()
  583. {
  584. $objectConstructor = new InitializedBlogPostConstructor();
  585. $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
  586. $post = new BlogPost('This is a nice title.', $author = new Author('Foo Bar'), new \DateTime('2011-07-30 00:00', new \DateTimeZone('UTC')), new Publisher('Bar Foo'));
  587. $this->setField($post, 'author', null);
  588. $this->setField($post, 'publisher', null);
  589. $this->assertEquals($this->getContent('blog_post_unauthored'), $this->serialize($post, SerializationContext::create()->setSerializeNull(true)));
  590. if ($this->hasDeserializer()) {
  591. $deserialized = $this->deserialize($this->getContent('blog_post_unauthored'), get_class($post), DeserializationContext::create()->setSerializeNull(true));
  592. $this->assertEquals('2011-07-30T00:00:00+0000', $this->getField($deserialized, 'createdAt')->format(\DateTime::ISO8601));
  593. $this->assertAttributeEquals('This is a nice title.', 'title', $deserialized);
  594. $this->assertAttributeSame(false, 'published', $deserialized);
  595. $this->assertAttributeSame(false, 'reviewed', $deserialized);
  596. $this->assertAttributeEquals(new ArrayCollection(), 'comments', $deserialized);
  597. $this->assertEquals(null, $this->getField($deserialized, 'author'));
  598. }
  599. }
  600. public function testExpressionAuthor()
  601. {
  602. $namingStrategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
  603. $evaluator = new ExpressionEvaluator(new ExpressionLanguage());
  604. $accessor = new ExpressionAccessorStrategy($evaluator, new DefaultAccessorStrategy());
  605. $this->serializationVisitors = new Map(array(
  606. 'json' => new JsonSerializationVisitor($namingStrategy, $accessor),
  607. 'xml' => new XmlSerializationVisitor($namingStrategy, $accessor),
  608. 'yml' => new YamlSerializationVisitor($namingStrategy, $accessor),
  609. ));
  610. $serializer = new Serializer($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher, null, $evaluator);
  611. $author = new AuthorExpressionAccess(123, "Ruud", "Kamphuis");
  612. $this->assertEquals($this->getContent('author_expression'), $serializer->serialize($author, $this->getFormat()));
  613. }
  614. /**
  615. * @expectedException \JMS\Serializer\Exception\ExpressionLanguageRequiredException
  616. * @expectedExceptionMessage The property firstName on JMS\Serializer\Tests\Fixtures\AuthorExpressionAccess requires the expression accessor strategy to be enabled.
  617. */
  618. public function testExpressionAccessorStrategNotEnabled()
  619. {
  620. $author = new AuthorExpressionAccess(123, "Ruud", "Kamphuis");
  621. $this->assertEquals($this->getContent('author_expression'), $this->serialize($author));
  622. }
  623. public function testReadOnly()
  624. {
  625. $author = new AuthorReadOnly(123, 'Ruud Kamphuis');
  626. $this->assertEquals($this->getContent('readonly'), $this->serialize($author));
  627. if ($this->hasDeserializer()) {
  628. $deserialized = $this->deserialize($this->getContent('readonly'), get_class($author));
  629. $this->assertNull($this->getField($deserialized, 'id'));
  630. $this->assertEquals('Ruud Kamphuis', $this->getField($deserialized, 'name'));
  631. }
  632. }
  633. public function testReadOnlyClass()
  634. {
  635. $author = new AuthorReadOnlyPerClass(123, 'Ruud Kamphuis');
  636. $this->assertEquals($this->getContent('readonly'), $this->serialize($author));
  637. if ($this->hasDeserializer()) {
  638. $deserialized = $this->deserialize($this->getContent('readonly'), get_class($author));
  639. $this->assertNull($this->getField($deserialized, 'id'));
  640. $this->assertEquals('Ruud Kamphuis', $this->getField($deserialized, 'name'));
  641. }
  642. }
  643. public function testPrice()
  644. {
  645. $price = new Price(3);
  646. $this->assertEquals($this->getContent('price'), $this->serialize($price));
  647. if ($this->hasDeserializer()) {
  648. $deserialized = $this->deserialize($this->getContent('price'), get_class($price));
  649. $this->assertEquals(3, $this->getField($deserialized, 'price'));
  650. }
  651. }
  652. public function testOrder()
  653. {
  654. $order = new Order(new Price(12.34));
  655. $this->assertEquals($this->getContent('order'), $this->serialize($order));
  656. if ($this->hasDeserializer()) {
  657. $this->assertEquals($order, $this->deserialize($this->getContent('order'), get_class($order)));
  658. }
  659. }
  660. public function testCurrencyAwarePrice()
  661. {
  662. $price = new CurrencyAwarePrice(2.34);
  663. $this->assertEquals($this->getContent('currency_aware_price'), $this->serialize($price));
  664. if ($this->hasDeserializer()) {
  665. $this->assertEquals($price, $this->deserialize($this->getContent('currency_aware_price'), get_class($price)));
  666. }
  667. }
  668. public function testOrderWithCurrencyAwarePrice()
  669. {
  670. $order = new CurrencyAwareOrder(new CurrencyAwarePrice(1.23));
  671. $this->assertEquals($this->getContent('order_with_currency_aware_price'), $this->serialize($order));
  672. if ($this->hasDeserializer()) {
  673. $this->assertEquals($order, $this->deserialize($this->getContent('order_with_currency_aware_price'), get_class($order)));
  674. }
  675. }
  676. /**
  677. * @group handlerCallback
  678. */
  679. public function testArticle()
  680. {
  681. $article = new Article();
  682. $article->element = 'custom';
  683. $article->value = 'serialized';
  684. $result = $this->serialize($article);
  685. $this->assertEquals($this->getContent('article'), $result);
  686. if ($this->hasDeserializer()) {
  687. $this->assertEquals($article, $this->deserialize($result, 'JMS\Serializer\Tests\Fixtures\Article'));
  688. }
  689. }
  690. public function testInline()
  691. {
  692. $inline = new InlineParent();
  693. $result = $this->serialize($inline);
  694. $this->assertEquals($this->getContent('inline'), $result);
  695. // no deserialization support
  696. }
  697. public function testInlineEmptyChild()
  698. {
  699. $inline = new InlineParent(new InlineChildEmpty());
  700. $result = $this->serialize($inline);
  701. $this->assertEquals($this->getContent('inline_child_empty'), $result);
  702. // no deserialization support
  703. }
  704. public function testEmptyChild()
  705. {
  706. // by empty object
  707. $inline = new ParentDoNotSkipWithEmptyChild(new InlineChildEmpty());
  708. $this->assertEquals($this->getContent('empty_child'), $this->serialize($inline));
  709. // by nulls
  710. $inner = new InlineChild();
  711. $inner->a = null;
  712. $inner->b = null;
  713. $inline = new ParentDoNotSkipWithEmptyChild($inner);
  714. $this->assertEquals($this->getContent('empty_child'), $this->serialize($inline));
  715. // by exclusion strategy
  716. $context = SerializationContext::create()->setGroups(['Default']);
  717. $inline = new ParentDoNotSkipWithEmptyChild(new InlineChildWithGroups());
  718. $this->assertEquals($this->getContent('empty_child'), $this->serialize($inline, $context));
  719. }
  720. public function testSkipEmptyChild()
  721. {
  722. // by empty object
  723. $inline = new ParentSkipWithEmptyChild(new InlineChildEmpty());
  724. $this->assertEquals($this->getContent('empty_child_skip'), $this->serialize($inline));
  725. // by nulls
  726. $inner = new InlineChild();
  727. $inner->a = null;
  728. $inner->b = null;
  729. $inline = new ParentSkipWithEmptyChild($inner);
  730. $this->assertEquals($this->getContent('empty_child_skip'), $this->serialize($inline));
  731. // by exclusion strategy
  732. $context = SerializationContext::create()->setGroups(['Default']);
  733. $inline = new ParentSkipWithEmptyChild(new InlineChildWithGroups());
  734. $this->assertEquals($this->getContent('empty_child_skip'), $this->serialize($inline, $context));
  735. }
  736. /**
  737. * @group log
  738. */
  739. public function testLog()
  740. {
  741. $this->assertEquals($this->getContent('log'), $this->serialize($log = new Log()));
  742. if ($this->hasDeserializer()) {
  743. $deserialized = $this->deserialize($this->getContent('log'), get_class($log));
  744. $this->assertEquals($log, $deserialized);
  745. }
  746. }
  747. public function testCircularReference()
  748. {
  749. $object = new CircularReferenceParent();
  750. $this->assertEquals($this->getContent('circular_reference'), $this->serialize($object));
  751. if ($this->hasDeserializer()) {
  752. $deserialized = $this->deserialize($this->getContent('circular_reference'), get_class($object));
  753. $col = $this->getField($deserialized, 'collection');
  754. $this->assertEquals(2, count($col));
  755. $this->assertEquals('child1', $col[0]->getName());
  756. $this->assertEquals('child2', $col[1]->getName());
  757. $this->assertSame($deserialized, $col[0]->getParent());
  758. $this->assertSame($deserialized, $col[1]->getParent());
  759. $col = $this->getField($deserialized, 'anotherCollection');
  760. $this->assertEquals(2, count($col));
  761. $this->assertEquals('child1', $col[0]->getName());
  762. $this->assertEquals('child2', $col[1]->getName());
  763. $this->assertSame($deserialized, $col[0]->getParent());
  764. $this->assertSame($deserialized, $col[1]->getParent());
  765. }
  766. }
  767. public function testLifecycleCallbacks()
  768. {
  769. $object = new ObjectWithLifecycleCallbacks();
  770. $this->assertEquals($this->getContent('lifecycle_callbacks'), $this->serialize($object));
  771. $this->assertAttributeSame(null, 'name', $object);
  772. if ($this->hasDeserializer()) {
  773. $deserialized = $this->deserialize($this->getContent('lifecycle_callbacks'), get_class($object));
  774. $this->assertEquals($object, $deserialized);
  775. }
  776. }
  777. public function testFormErrors()
  778. {
  779. $errors = array(
  780. new FormError('This is the form error'),
  781. new FormError('Another error')
  782. );
  783. $this->assertEquals($this->getContent('form_errors'), $this->serialize($errors));
  784. }
  785. public function testNestedFormErrors()
  786. {
  787. $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
  788. $formConfigBuilder = new \Symfony\Component\Form\FormConfigBuilder('foo', null, $dispatcher);
  789. $formConfigBuilder->setCompound(true);
  790. $formConfigBuilder->setDataMapper($this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock());
  791. $fooConfig = $formConfigBuilder->getFormConfig();
  792. $form = new Form($fooConfig);
  793. $form->addError(new FormError('This is the form error'));
  794. $formConfigBuilder = new \Symfony\Component\Form\FormConfigBuilder('bar', null, $dispatcher);
  795. $barConfig = $formConfigBuilder->getFormConfig();
  796. $child = new Form($barConfig);
  797. $child->addError(new FormError('Error of the child form'));
  798. $form->add($child);
  799. $this->assertEquals($this->getContent('nested_form_errors'), $this->serialize($form));
  800. }
  801. public function testFormErrorsWithNonFormComponents()
  802. {
  803. if (!class_exists('Symfony\Component\Form\Extension\Core\Type\SubmitType')) {
  804. $this->markTestSkipped('Not using Symfony Form >= 2.3 with submit type');
  805. }
  806. $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock();
  807. $factoryBuilder = new FormFactoryBuilder();
  808. $factoryBuilder->addType(new \Symfony\Component\Form\Extension\Core\Type\SubmitType);
  809. $factoryBuilder->addType(new \Symfony\Component\Form\Extension\Core\Type\ButtonType);
  810. $factory = $factoryBuilder->getFormFactory();
  811. $formConfigBuilder = new \Symfony\Component\Form\FormConfigBuilder('foo', null, $dispatcher);
  812. $formConfigBuilder->setFormFactory($factory);
  813. $formConfigBuilder->setCompound(true);
  814. $formConfigBuilder->setDataMapper($this->getMockBuilder('Symfony\Component\Form\DataMapperInterface')->getMock());
  815. $fooConfig = $formConfigBuilder->getFormConfig();
  816. $form = new Form($fooConfig);
  817. $form->add('save', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class);
  818. try {
  819. $this->serialize($form);
  820. } catch (\Exception $e) {
  821. $this->assertTrue(false, 'Serialization should not throw an exception');
  822. }
  823. }
  824. public function testConstraintViolation()
  825. {
  826. $violation = new ConstraintViolation('Message of violation', 'Message of violation', array(), null, 'foo', null);
  827. $this->assertEquals($this->getContent('constraint_violation'), $this->serialize($violation));
  828. }
  829. public function testConstraintViolationList()
  830. {
  831. $violations = new ConstraintViolationList();
  832. $violations->add(new ConstraintViolation('Message of violation', 'Message of violation', array(), null, 'foo', null));
  833. $violations->add(new ConstraintViolation('Message of another violation', 'Message of another violation', array(), null, 'bar', null));
  834. $this->assertEquals($this->getContent('constraint_violation_list'), $this->serialize($violations));
  835. }
  836. public function testDoctrineProxy()
  837. {
  838. if (!class_exists('Doctrine\ORM\Version')) {
  839. $this->markTestSkipped('Doctrine is not available.');
  840. }
  841. $object = new SimpleObjectProxy('foo', 'bar');
  842. $this->assertEquals($this->getContent('orm_proxy'), $this->serialize($object));
  843. }
  844. public function testInitializedDoctrineProxy()
  845. {
  846. if (!class_exists('Doctrine\ORM\Version')) {
  847. $this->markTestSkipped('Doctrine is not available.');
  848. }
  849. $object = new SimpleObjectProxy('foo', 'bar');
  850. $object->__load();
  851. $this->assertEquals($this->getContent('orm_proxy'), $this->serialize($object));
  852. }
  853. public function testCustomAccessor()
  854. {
  855. $post = new IndexedCommentsBlogPost();
  856. $this->assertEquals($this->getContent('custom_accessor'), $this->serialize($post));
  857. }
  858. public function testMixedAccessTypes()
  859. {
  860. $object = new GetSetObject();
  861. $this->assertEquals($this->getContent('mixed_access_types'), $this->serialize($object));
  862. if ($this->hasDeserializer()) {
  863. $object = $this->deserialize($this->getContent('mixed_access_types'), 'JMS\Serializer\Tests\Fixtures\GetSetObject');
  864. $this->assertAttributeEquals(1, 'id', $object);
  865. $this->assertAttributeEquals('Johannes', 'name', $object);
  866. $this->assertAttributeEquals(42, 'readOnlyProperty', $object);
  867. }
  868. }
  869. public function testAccessorOrder()
  870. {
  871. $this->assertEquals($this->getContent('accessor_order_child'), $this->serialize(new AccessorOrderChild()));
  872. $this->assertEquals($this->getContent('accessor_order_parent'), $this->serialize(new AccessorOrderParent()));
  873. $this->assertEquals($this->getContent('accessor_order_methods'), $this->serialize(new AccessorOrderMethod()));
  874. }
  875. public function testGroups()
  876. {
  877. $groupsObject = new GroupsObject();
  878. $this->assertEquals($this->getContent('groups_all'), $this->serializer->serialize($groupsObject, $this->getFormat()));
  879. $this->assertEquals(
  880. $this->getContent('groups_foo'),
  881. $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array('foo')))
  882. );
  883. $this->assertEquals(
  884. $this->getContent('groups_foobar'),
  885. $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array('foo', 'bar')))
  886. );
  887. $this->assertEquals(
  888. $this->getContent('groups_all'),
  889. $this->serializer->serialize($groupsObject, $this->getFormat())
  890. );
  891. $this->assertEquals(
  892. $this->getContent('groups_default'),
  893. $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array(GroupsExclusionStrategy::DEFAULT_GROUP)))
  894. );
  895. $this->assertEquals(
  896. $this->getContent('groups_default'),
  897. $this->serializer->serialize($groupsObject, $this->getFormat(), SerializationContext::create()->setGroups(array(GroupsExclusionStrategy::DEFAULT_GROUP)))
  898. );
  899. }
  900. public function testAdvancedGroups()
  901. {
  902. $adrien = new GroupsUser(
  903. 'John',
  904. new GroupsUser(
  905. 'John Manager',
  906. null,
  907. array(
  908. new GroupsUser(
  909. 'John Manager friend 1',
  910. new GroupsUser('John Manager friend 1 manager')
  911. ),
  912. new GroupsUser('John Manager friend 2'),
  913. )
  914. ),
  915. array(
  916. new GroupsUser(
  917. 'John friend 1',
  918. new GroupsUser('John friend 1 manager')
  919. ),
  920. new GroupsUser(
  921. 'John friend 2',
  922. new GroupsUser('John friend 2 manager')
  923. )
  924. )
  925. );
  926. $this->assertEquals(
  927. $this->getContent('groups_advanced'),
  928. $this->serializer->serialize(
  929. $adrien,
  930. $this->getFormat(),
  931. SerializationContext::create()->setGroups(array(
  932. GroupsExclusionStrategy::DEFAULT_GROUP,
  933. 'manager_group',
  934. 'friends_group',
  935. 'manager' => array(
  936. GroupsExclusionStrategy::DEFAULT_GROUP,
  937. 'friends_group',
  938. 'friends' => array('nickname_group'),
  939. ),
  940. 'friends' => array(
  941. 'manager_group'
  942. )
  943. ))
  944. )
  945. );
  946. }
  947. /**
  948. * @expectedException JMS\Serializer\Exception\InvalidArgumentException
  949. * @expectedExceptionMessage Invalid group name "foo, bar" on "JMS\Serializer\Tests\Fixtures\InvalidGroupsObject->foo", did you mean to create multiple groups?
  950. */
  951. public function testInvalidGroupName()
  952. {
  953. $groupsObject = new InvalidGroupsObject();
  954. $this->serializer->serialize($groupsObject, $this->getFormat());
  955. }
  956. public function testVirtualProperty()
  957. {
  958. $this->assertEquals($this->getContent('virtual_properties'), $this->serialize(new ObjectWithVirtualProperties()));
  959. }
  960. public function testVirtualVersions()
  961. {
  962. $this->assertEquals(
  963. $this->getContent('virtual_properties_low'),
  964. $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(2))
  965. );
  966. $this->assertEquals(
  967. $this->getContent('virtual_properties_all'),
  968. $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(7))
  969. );
  970. $this->assertEquals(
  971. $this->getContent('virtual_properties_high'),
  972. $this->serialize(new ObjectWithVersionedVirtualProperties(), SerializationContext::create()->setVersion(9))
  973. );
  974. }
  975. public function testCustomHandler()
  976. {
  977. if (!$this->hasDeserializer()) {
  978. return;
  979. }
  980. $handler = function () {
  981. return new CustomDeserializationObject('customly_unserialized_value');
  982. };
  983. $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_DESERIALIZATION, 'CustomDeserializationObject', $this->getFormat(), $handler);
  984. $serialized = $this->serializer->serialize(new CustomDeserializationObject('sometext'), $this->getFormat());
  985. $object = $this->serializer->deserialize($serialized, 'CustomDeserializationObject', $this->getFormat());
  986. $this->assertEquals('customly_unserialized_value', $object->someProperty);
  987. }
  988. public function testInput()
  989. {
  990. $this->assertEquals($this->getContent('input'), $this->serializer->serialize(new Input(), $this->getFormat()));
  991. }
  992. public function testObjectWithEmptyHash()
  993. {
  994. $this->assertEquals($this->getContent('hash_empty'), $this->serializer->serialize(new ObjectWithEmptyHash(), $this->getFormat()));
  995. }
  996. /**
  997. * @group null
  998. */
  999. public function testSerializeObjectWhenNull()
  1000. {
  1001. $this->assertEquals(
  1002. $this->getContent('object_when_null'),
  1003. $this->serialize(new Comment(null, 'foo'), SerializationContext::create()->setSerializeNull(false))
  1004. );
  1005. $this->assertEquals(
  1006. $this->getContent('object_when_null_and_serialized'),
  1007. $this->serialize(new Comment(null, 'foo'), SerializationContext::create()->setSerializeNull(true))
  1008. );
  1009. }
  1010. /**
  1011. * @group polymorphic
  1012. */
  1013. public function testPolymorphicObjectsWithGroup()
  1014. {
  1015. $context = SerializationContext::create();
  1016. $context->setGroups(array("foo"));
  1017. $this->assertEquals(
  1018. $this->getContent('car'),
  1019. $this->serialize(new \JMS\Serializer\Tests\Fixtures\DiscriminatorGroup\Car(5), $context)
  1020. );
  1021. }
  1022. /**
  1023. * @group polymorphic
  1024. */
  1025. public function testPolymorphicObjects()
  1026. {
  1027. $this->assertEquals(
  1028. $this->getContent('car'),
  1029. $this->serialize(new Car(5))
  1030. );
  1031. self::assertEquals(
  1032. $this->getContent('post'),
  1033. $this->serialize(new Post('Post Title'))
  1034. );
  1035. self::assertEquals(
  1036. $this->getContent('image_post'),
  1037. $this->serialize(new ImagePost('Image Post Title'))
  1038. );
  1039. if ($this->hasDeserializer()) {
  1040. $this->assertEquals(
  1041. new Car(5),
  1042. $this->deserialize(
  1043. $this->getContent('car'),
  1044. 'JMS\Serializer\Tests\Fixtures\Discriminator\Car'
  1045. ),
  1046. 'Class is resolved correctly when concrete sub-class is used.'
  1047. );
  1048. $this->assertEquals(
  1049. new Car(5),
  1050. $this->deserialize(
  1051. $this->getContent('car'),
  1052. 'JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle'
  1053. ),
  1054. 'Class is resolved correctly when least supertype is used.'
  1055. );
  1056. $this->assertEquals(
  1057. new Car(5),
  1058. $this->deserialize(
  1059. $this->getContent('car_without_type'),
  1060. 'JMS\Serializer\Tests\Fixtures\Discriminator\Car'
  1061. ),
  1062. 'Class is resolved correctly when concrete sub-class is used and no type is defined.'
  1063. );
  1064. self::assertEquals(
  1065. new Post('Post Title'),
  1066. $this->deserialize(
  1067. $this->getContent('post'),
  1068. 'JMS\Serializer\Tests\Fixtures\Discriminator\Post'
  1069. ),
  1070. 'Class is resolved correctly when parent class is used and type is set.'
  1071. );
  1072. self::assertEquals(
  1073. new ImagePost('Image Post Title'),
  1074. $this->deserialize(
  1075. $this->getContent('image_post'),
  1076. 'JMS\Serializer\Tests\Fixtures\Discriminator\Post'
  1077. ),
  1078. 'Class is resolved correctly when least supertype is used.'
  1079. );
  1080. self::assertEquals(
  1081. new ImagePost('Image Post Title'),
  1082. $this->deserialize(
  1083. $this->getContent('image_post'),
  1084. 'JMS\Serializer\Tests\Fixtures\Discriminator\ImagePost'
  1085. ),
  1086. 'Class is resolved correctly when concrete sub-class is used and no type is defined.'
  1087. );
  1088. }
  1089. }
  1090. /**
  1091. * @group polymorphic
  1092. */
  1093. public function testNestedPolymorphicObjects()
  1094. {
  1095. $garage = new Garage(array(new Car(3), new Moped(1)));
  1096. $this->assertEquals(
  1097. $this->getContent('garage'),
  1098. $this->serialize($garage)
  1099. );
  1100. if ($this->hasDeserializer()) {
  1101. $this->assertEquals(
  1102. $garage,
  1103. $this->deserialize(
  1104. $this->getContent('garage'),
  1105. 'JMS\Serializer\Tests\Fixtures\Garage'
  1106. )
  1107. );
  1108. }
  1109. }
  1110. /**
  1111. * @group polymorphic
  1112. */
  1113. public function testNestedPolymorphicInterfaces()
  1114. {
  1115. $garage = new VehicleInterfaceGarage(array(new Car(3), new Moped(1)));
  1116. $this->assertEquals(
  1117. $this->getContent('garage'),
  1118. $this->serialize($garage)
  1119. );
  1120. if ($this->hasDeserializer()) {
  1121. $this->assertEquals(
  1122. $garage,
  1123. $this->deserialize(
  1124. $this->getContent('garage'),
  1125. 'JMS\Serializer\Tests\Fixtures\VehicleInterfaceGarage'
  1126. )
  1127. );
  1128. }
  1129. }
  1130. /**
  1131. * @group polymorphic
  1132. * @expectedException LogicException
  1133. */
  1134. public function testPolymorphicObjectsInvalidDeserialization()
  1135. {
  1136. if (!$this->hasDeserializer()) {
  1137. throw new \LogicException('No deserializer');
  1138. }
  1139. $this->deserialize(
  1140. $this->getContent('car_without_type'),
  1141. 'JMS\Serializer\Tests\Fixtures\Discriminator\Vehicle'
  1142. );
  1143. }
  1144. public function testDepthExclusionStrategy()
  1145. {
  1146. $context = SerializationContext::create()
  1147. ->addExclusionStrategy(new DepthExclusionStrategy());
  1148. $data = new Tree(
  1149. new Node(array(
  1150. new Node(array(
  1151. new Node(array(
  1152. new Node(array(
  1153. new Node(),
  1154. )),
  1155. )),
  1156. )),
  1157. ))
  1158. );
  1159. $this->assertEquals($this->getContent('tree'), $this->serializer->serialize($data, $this->getFormat(), $context));
  1160. }
  1161. public function testMaxDepthWithSkippableObject()
  1162. {
  1163. $data = new Gh236Foo();
  1164. $context = SerializationContext::create()->enableMaxDepthChecks();
  1165. $serialized = $this->serialize($data, $context);
  1166. $this->assertEquals($this->getContent('maxdepth_skippabe_object'), $serialized);
  1167. }
  1168. public function testDeserializingIntoExistingObject()
  1169. {
  1170. if (!$this->hasDeserializer()) {
  1171. return;
  1172. }
  1173. $objectConstructor = new InitializedObjectConstructor(new UnserializeObjectConstructor());
  1174. $serializer = new Serializer(
  1175. $this->factory, $this->handlerRegistry, $objectConstructor,
  1176. $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher
  1177. );
  1178. $order = new Order(new Price(12));
  1179. $context = new DeserializationContext();
  1180. $context->attributes->set('target', $order);
  1181. $deseralizedOrder = $serializer->deserialize(
  1182. $this->getContent('order'),
  1183. get_class($order),
  1184. $this->getFormat(),
  1185. $context
  1186. );
  1187. $this->assertSame($order, $deseralizedOrder);
  1188. $this->assertEquals(new Order(new Price(12.34)), $deseralizedOrder);
  1189. $this->assertAttributeInstanceOf('JMS\Serializer\Tests\Fixtures\Price', 'cost', $deseralizedOrder);
  1190. }
  1191. public function testObjectWithNullableArrays()
  1192. {
  1193. $object = new ObjectWithEmptyNullableAndEmptyArrays();
  1194. $this->assertEquals($this->getContent('nullable_arrays'), $this->serializer->serialize($object, $this->getFormat()));
  1195. }
  1196. abstract protected function getContent($key);
  1197. abstract protected function getFormat();
  1198. protected function hasDeserializer()
  1199. {
  1200. return true;
  1201. }
  1202. protected function serialize($data, Context $context = null)
  1203. {
  1204. return $this->serializer->serialize($data, $this->getFormat(), $context);
  1205. }
  1206. protected function deserialize($content, $type, Context $context = null)
  1207. {
  1208. return $this->serializer->deserialize($content, $type, $this->getFormat(), $context);
  1209. }
  1210. protected function setUp()
  1211. {
  1212. $this->factory = new MetadataFactory(new AnnotationDriver(new AnnotationReader()));
  1213. $this->handlerRegistry = new HandlerRegistry();
  1214. $this->handlerRegistry->registerSubscribingHandler(new ConstraintViolationHandler());
  1215. $this->handlerRegistry->registerSubscribingHandler(new StdClassHandler());
  1216. $this->handlerRegistry->registerSubscribingHandler(new DateHandler());
  1217. $this->handlerRegistry->registerSubscribingHandler(new FormErrorHandler(new IdentityTranslator(new MessageSelector())));
  1218. $this->handlerRegistry->registerSubscribingHandler(new PhpCollectionHandler());
  1219. $this->handlerRegistry->registerSubscribingHandler(new ArrayCollectionHandler());
  1220. $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_SERIALIZATION, 'AuthorList', $this->getFormat(),
  1221. function (VisitorInterface $visitor, $object, array $type, Context $context) {
  1222. return $visitor->visitArray(iterator_to_array($object), $type, $context);
  1223. }
  1224. );
  1225. $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_DESERIALIZATION, 'AuthorList', $this->getFormat(),
  1226. function (VisitorInterface $visitor, $data, $type, Context $context) {
  1227. $type = array(
  1228. 'name' => 'array',
  1229. 'params' => array(
  1230. array('name' => 'integer', 'params' => array()),
  1231. array('name' => 'JMS\Serializer\Tests\Fixtures\Author', 'params' => array()),
  1232. ),
  1233. );
  1234. $elements = $visitor->getNavigator()->accept($data, $type, $context);
  1235. $list = new AuthorList();
  1236. foreach ($elements as $author) {
  1237. $list->add($author);
  1238. }
  1239. return $list;
  1240. }
  1241. );
  1242. $this->dispatcher = new EventDispatcher();
  1243. $this->dispatcher->addSubscriber(new DoctrineProxySubscriber());
  1244. $namingStrategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
  1245. $this->objectConstructor = new UnserializeObjectConstructor();
  1246. $this->serializationVisitors = new Map(array(
  1247. 'json' => new JsonSerializationVisitor($namingStrategy),
  1248. 'xml' => new XmlSerializationVisitor($namingStrategy),
  1249. 'yml' => new YamlSerializationVisitor($namingStrategy),
  1250. ));
  1251. $this->deserializationVisitors = new Map(array(
  1252. 'json' => new JsonDeserializationVisitor($namingStrategy),
  1253. 'xml' => new XmlDeserializationVisitor($namingStrategy),
  1254. ));
  1255. $this->serializer = new Serializer($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->serializationVisitors, $this->deserializationVisitors, $this->dispatcher);
  1256. }
  1257. protected function getField($obj, $name)
  1258. {
  1259. $ref = new \ReflectionProperty($obj, $name);
  1260. $ref->setAccessible(true);
  1261. return $ref->getValue($obj);
  1262. }
  1263. private function setField($obj, $name, $value)
  1264. {
  1265. $ref = new \ReflectionProperty($obj, $name);
  1266. $ref->setAccessible(true);
  1267. $ref->setValue($obj, $value);
  1268. }
  1269. }