38

I am attempting to use JMSSerializer as a stand alone library to map JSON responses from an API to my model classes and am running into some issues.

Executing the following code results in an exception:

<?php
require dirname(__DIR__) . '/vendor/autoload.php';

use JMS\Serializer\Annotation AS JMS;

class Trii {
    /**
     * User ID for this session
     * @JMS\SerializedName("userID")
     * @JMS\Annotation(getter="getUserId")
     * @JMS\Type("string")
     * @var string
     */
    private $userId;

    public function getUserId() {
        return $this->userId;
    }

    public function setUserId($userId) {
        $this->userId = $userId;
    }
}

$serializer = \JMS\Serializer\SerializerBuilder::create()->setDebug(true)->build();
$object = $serializer->deserialize('{"userID":"Trii"}', 'Trii', 'json');
var_dump($object);
?>

Here is the exception

Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The annotation "@JMS\Serializer\Annotation\SerializedName" in property Trii::$userId does not exist, or could not be auto-loaded.

I have the following libraries installed for the project via composer

{
    "require": {
        "jms/serializer": "1.0.*@dev"
    }
}

Is there something obvious I am missing since I am not using the whole Doctrine 2 solution?

EDIT: my final solution was to create a bootstrap file with the following content:

<?php
// standard composer install vendor autoload magic
require dirname(__DIR__) . '/vendor/autoload.php';

// Bootstrap the JMS custom annotations for Object to Json mapping
\Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
    'JMS\Serializer\Annotation',
    dirname(__DIR__).'/vendor/jms/serializer/src'
);
?>
Josh J
  • 6,813
  • 3
  • 25
  • 47

6 Answers6

73

Pretty sure this enables silent auto-loading which is much more convenient than registering the namespaces yourself.

AnnotationRegistry::registerLoader('class_exists');
greg0ire
  • 22,714
  • 16
  • 72
  • 101
Flip
  • 4,778
  • 1
  • 34
  • 48
  • This helped me! Thanks! – Andresch Serj Jun 01 '15 at 13:55
  • 2
    Make sure to add a `use Doctrine\Common\Annotations\AnnotationRegistry;` line if you're going to use the short form for this. I put this in my application bootrapper (`/app/boostrap/app.php`), although it could go in the autoloader (`/app/boostrap/autoload.php`) as well as long as it follows the composer autoload. – Leith Sep 03 '16 at 05:32
  • This is the smartest solution! Thanks bro! – Tecnocat Sep 30 '16 at 15:08
  • Adding `Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');` in `bootstrap/app.php` of my Laravel Lumen project did work! – Sithu Jun 17 '17 at 05:37
  • This by far was the simplest and best way to do this. – Jason Roman Sep 16 '17 at 23:59
40

I ran into the same problem and found your question through Google. Unfortunately you hadn't yet received any answers, so I had to dig in myself. :P

The thing is, Doctrine Annotations, which JMSSerializer Annotations uses, does NOT use normal PHP autoloading.

How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the AnnotationReader sets the second parameter $autoload of class_exists($name, $autoload) to false. To work flawlessly the AnnotationReader requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT part of the PSR-0 specification for autoloading.

This means you have to register the Annotation file(s) yourself:

AnnotationRegistry::registerFile(
   <PROJECT ROOT> . 
   "/vendor/jms/serializer/src/JMS/Serializer/Annotation/SerializedName.php");

... or the whole namespace (preferred method):

AnnotationRegistry::registerAutoloadNamespace(
    'JMS\Serializer\Annotation', 
    <PROJECT ROOT> . "/vendor/jms/serializer/src");

Be careful with the path in registerAutoloadNamespace. I first tried to register the whole path to annotations in the same manner with registerFile:

<PROJECT ROOT> . "/vendor/jms/serializer/src/JMS/Serializer/Annotation 

but that failed miserably. :D

I hope this gets you a step further. :)

Peyman Mohamadpour
  • 17,954
  • 24
  • 89
  • 100
SirArturio
  • 400
  • 5
  • 4
4

@SirArturio has the correct answer to this Autoloading puzzle, and I just wanted to add a touch more clarity in response to @messified or anyone else struggling to get this working. As he eloquently explained, the automatic PSR-0 handler in composer, or SPL isn't going to cut it for loading these annotations since they use Doctrine's autoloading.

So here is small complete example. Whenever you create your JMS Serializer object to begin serialization is a good time to add the annotation namespace to doctrine's autoloader. For clarity sake I'm assuming no IoC, and fully qualified namespaces (hint hint, use dependency injection):

<?php
Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation', 
$your_app_basepath . "/vendor/jms/serializer/src");


$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$json_output = $serializer->serialize('MyProject\MyClass', 'json');

Then in your MyProject\MyClass:

<?php
use JMS\Serializer\Annotation as JMS;

class MyClass{

    /** @JMS\Exclude */
    private $something_secret;
}

And that should cut it, autoloading the proper annotation file using doctrine instead of composer.

jpschroeder
  • 6,636
  • 2
  • 34
  • 34
3

Check the capitalisation of your annotations. I had a similar problem when deploying from a Windows dev environment to an Ubuntu server which was caused by a typo in the case of my annotation. Windows files are case-insensitive so it works there but fails on Linux.

Tamlyn
  • 22,122
  • 12
  • 111
  • 127
0

If you use Composer, you can get loader by specifying a path in require:

$loader = require(__DIR__ . '/../vendor/autoload.php');
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
Lebnik
  • 628
  • 8
  • 11
-3

Here is the solution

1.go to php directory then install composer php composer-setup.php 2. go to project sdk directory e.g.

cd /Applications/XAMPP/xamppfiles/htdocs/streetreturn/adn_sdk-php-master

update composer to install dependencies php /Users/zakir/composer.phar update

*Note: /Users/zakir/composer.phar will be located when install composer in step 1

  • 1
    This question was already answered and your answer does not address the question or clarify another solution. – Josh J Mar 02 '17 at 18:12