Selaa lähdekoodia

backwards compatible config refactoring (fixes #30)

Johannes Schmitt 13 vuotta sitten
vanhempi
commit
f4e76640a0

+ 0 - 56
DependencyInjection/Compiler/SetCustomHandlersPass.php

@@ -1,56 +0,0 @@
-<?php
-
-/*
- * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace JMS\SerializerBundle\DependencyInjection\Compiler;
-
-use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-
-class SetCustomHandlersPass implements CompilerPassInterface
-{
-    public function process(ContainerBuilder $container)
-    {
-        $serializationHandlers = array();
-        foreach ($container->findTaggedServiceIds('jms_serializer.serialization_handler') as $id => $attributes) {
-            $serializationHandlers[] = new Reference($id);
-        }
-
-        $deserializationHandlers = array();
-        foreach ($container->findTaggedServiceIds('jms_serializer.deserialization_handler') as $id => $attributes) {
-            $deserializationHandlers[] = new Reference($id);
-        }
-
-        $container
-            ->getDefinition('jms_serializer.json_serialization_visitor')
-            ->replaceArgument(1, $serializationHandlers)
-        ;
-        $container
-            ->getDefinition('jms_serializer.xml_serialization_visitor')
-            ->replaceArgument(1, $serializationHandlers)
-        ;
-        $container
-            ->getDefinition('jms_serializer.json_deserialization_visitor')
-            ->replaceArgument(1, $deserializationHandlers)
-        ;
-        $container
-            ->getDefinition('jms_serializer.xml_deserialization_visitor')
-            ->replaceArgument(1, $deserializationHandlers)
-        ;
-    }
-}

+ 56 - 55
DependencyInjection/Configuration.php

@@ -18,87 +18,88 @@
 
 namespace JMS\SerializerBundle\DependencyInjection;
 
+use Symfony\Component\Config\Definition\Builder\NodeBuilder;
 use Symfony\Component\Config\Definition\Builder\TreeBuilder;
 use Symfony\Component\Config\Definition\ConfigurationInterface;
 
 class Configuration implements ConfigurationInterface
 {
     private $debug;
+    private $factories;
 
-    public function __construct($debug = false)
+    public function __construct($debug = false, array $factories = array())
     {
         $this->debug = $debug;
+        $this->factories = $factories;
     }
 
     public function getConfigTreeBuilder()
     {
         $tb = new TreeBuilder();
 
-        $tb
+        $root = $tb
             ->root('jms_serializer', 'array')
                 ->children()
-                    ->arrayNode('property_naming')
-                        ->addDefaultsIfNotSet()
-                        ->children()
-                            ->scalarNode('id')->cannotBeEmpty()->end()
-                            ->scalarNode('separator')->defaultValue('_')->end()
-                            ->booleanNode('lower_case')->defaultTrue()->end()
-                            ->booleanNode('enable_cache')->defaultTrue()->end()
-                        ->end()
-                    ->end()
-                    ->arrayNode('handlers')
+        ;
+
+        $this->addSerializersSection($root);
+        $this->addMetadataSection($root);
+
+        return $tb;
+    }
+
+    private function addSerializersSection(NodeBuilder $builder)
+    {
+        $builder
+            ->arrayNode('property_naming')
+                ->addDefaultsIfNotSet()
+                ->children()
+                    ->scalarNode('id')->cannotBeEmpty()->end()
+                    ->scalarNode('separator')->defaultValue('_')->end()
+                    ->booleanNode('lower_case')->defaultTrue()->end()
+                    ->booleanNode('enable_cache')->defaultTrue()->end()
+                ->end()
+            ->end()
+        ;
+
+        $handlerNode = $builder
+            ->arrayNode('handlers')
+                ->addDefaultsIfNotSet()
+                ->children()
+        ;
+
+        foreach ($this->factories as $factory) {
+            $factory->addConfiguration(
+                $handlerNode->arrayNode($factory->getConfigKey())->canBeUnset());
+        }
+    }
+
+    private function addMetadataSection(NodeBuilder $builder)
+    {
+        $builder
+            ->arrayNode('metadata')
+                ->addDefaultsIfNotSet()
+                ->fixXmlConfig('directory', 'directories')
+                ->children()
+                    ->scalarNode('cache')->defaultValue('file')->end()
+                    ->booleanNode('debug')->defaultValue($this->debug)->end()
+                    ->arrayNode('file_cache')
                         ->addDefaultsIfNotSet()
                         ->children()
-                            ->arrayNode('object_based')
-                                ->treatTrueLike(array('serialization' => true, 'deserialization' => true))
-                                ->treatNullLike(array('serialization' => true, 'deserialization' => true))
-                                ->treatFalseLike(array('serialization' => false, 'deserialization' => false))
-                                ->addDefaultsIfNotSet()
-                                ->children()
-                                    ->booleanNode('serialization')->defaultFalse()->end()
-                                    ->booleanNode('deserialization')->defaultFalse()->end()
-                                ->end()
-                            ->end()
-                            ->arrayNode('datetime')
-                                ->addDefaultsIfNotSet()
-                                ->canBeUnset()
-                                ->children()
-                                    ->scalarNode('format')->defaultValue(\DateTime::ISO8601)->end()
-                                    ->scalarNode('default_timezone')->defaultValue(date_default_timezone_get())->end()
-                                ->end()
-                            ->end()
-                            ->booleanNode('array_collection')->defaultTrue()->end()
-                            ->booleanNode('form_error')->defaultTrue()->end()
-                            ->booleanNode('constraint_violation')->defaultTrue()->end()
+                            ->scalarNode('dir')->defaultValue('%kernel.cache_dir%/serializer')->end()
                         ->end()
                     ->end()
-                    ->arrayNode('metadata')
-                        ->addDefaultsIfNotSet()
-                        ->fixXmlConfig('directory', 'directories')
-                        ->children()
-                            ->scalarNode('cache')->defaultValue('file')->end()
-                            ->booleanNode('debug')->defaultValue($this->debug)->end()
-                            ->arrayNode('file_cache')
-                                ->addDefaultsIfNotSet()
-                                ->children()
-                                    ->scalarNode('dir')->defaultValue('%kernel.cache_dir%/serializer')->end()
-                                ->end()
-                            ->end()
-                            ->booleanNode('auto_detection')->defaultTrue()->end()
-                            ->arrayNode('directories')
-                                ->prototype('array')
-                                    ->children()
-                                        ->scalarNode('path')->isRequired()->end()
-                                        ->scalarNode('namespace_prefix')->defaultValue('')->end()
-                                    ->end()
-                                ->end()
+                    ->booleanNode('auto_detection')->defaultTrue()->end()
+                    ->arrayNode('directories')
+                        ->prototype('array')
+                            ->children()
+                                ->scalarNode('path')->isRequired()->end()
+                                ->scalarNode('namespace_prefix')->defaultValue('')->end()
                             ->end()
                         ->end()
                     ->end()
                 ->end()
             ->end()
         ;
-
-        return $tb;
     }
 }

+ 33 - 0
DependencyInjection/Factory/ArrayCollectionFactory.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Factory;
+
+use JMS\SerializerBundle\DependencyInjection\HandlerDefinitionFactoryInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+class ArrayCollectionFactory implements HandlerDefinitionFactoryInterface
+{
+    public function getConfigKey()
+    {
+        return 'array_collection';
+    }
+
+    public function getType(array $config)
+    {
+        return self::TYPE_DESERIALIZATION;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->addDefaultsIfNotSet()
+            ->defaultValue(array())
+        ;
+    }
+
+    public function getHandlerId(ContainerBuilder $container, array $config)
+    {
+        return 'jms_serializer.array_collection_handler';
+    }
+}

+ 33 - 0
DependencyInjection/Factory/ConstraintViolationFactory.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Factory;
+
+use JMS\SerializerBundle\DependencyInjection\HandlerDefinitionFactoryInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+class ConstraintViolationFactory implements HandlerDefinitionFactoryInterface
+{
+    public function getConfigKey()
+    {
+        return 'constraint_violation';
+    }
+
+    public function getType(array $config)
+    {
+        return self::TYPE_SERIALIZATION;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->addDefaultsIfNotSet()
+            ->defaultValue(array())
+        ;
+    }
+
+    public function getHandlerId(ContainerBuilder $container, array $config)
+    {
+        return 'jms_serializer.constraint_violation_handler';
+    }
+}

+ 43 - 0
DependencyInjection/Factory/DateTimeFactory.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Factory;
+
+use JMS\SerializerBundle\DependencyInjection\HandlerDefinitionFactoryInterface;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class DateTimeFactory implements HandlerDefinitionFactoryInterface
+{
+    public function getConfigKey()
+    {
+        return 'datetime';
+    }
+
+    public function getType(array $config)
+    {
+        return self::TYPE_ALL;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->addDefaultsIfNotSet()
+            ->canBeUnset()
+            ->children()
+                ->scalarNode('format')->defaultValue(\DateTime::ISO8601)->end()
+                ->scalarNode('default_timezone')->defaultValue(date_default_timezone_get())->end()
+            ->end()
+        ;
+    }
+
+    public function getHandlerId(ContainerBuilder $container, array $config)
+    {
+        $container
+            ->getDefinition('jms_serializer.datetime_handler')
+            ->addArgument($config['format'])
+            ->addArgument($config['default_timezone'])
+        ;
+
+        return 'jms_serializer.datetime_handler';
+    }
+}

+ 33 - 0
DependencyInjection/Factory/FormErrorFactory.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Factory;
+
+use JMS\SerializerBundle\DependencyInjection\HandlerDefinitionFactoryInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+class FormErrorFactory implements HandlerDefinitionFactoryInterface
+{
+    public function getConfigKey()
+    {
+        return 'form_error';
+    }
+
+    public function getType(array $config)
+    {
+        return self::TYPE_SERIALIZATION;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $builder)
+    {
+        $builder
+            ->addDefaultsIfNotSet()
+            ->defaultValue(array())
+        ;
+    }
+
+    public function getHandlerId(ContainerBuilder $container, array $config)
+    {
+        return 'jms_serializer.form_error_handler';
+    }
+}

+ 48 - 0
DependencyInjection/Factory/ObjectBasedFactory.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection\Factory;
+
+use JMS\SerializerBundle\DependencyInjection\HandlerDefinitionFactoryInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+class ObjectBasedFactory implements HandlerDefinitionFactoryInterface
+{
+    public function getConfigKey()
+    {
+        return 'object_based';
+    }
+
+    public function getType(array $config)
+    {
+        $type = 0;
+
+        if ($config['serialization']) {
+            $type |= self::TYPE_SERIALIZATION;
+        }
+        if ($config['deserialization']) {
+            $type |= self::TYPE_DESERIALIZATION;
+        }
+
+        return $type;
+    }
+
+    public function addConfiguration(ArrayNodeDefinition $node)
+    {
+        $node
+            ->treatTrueLike(array('serialization' => true, 'deserialization' => true))
+            ->treatNullLike(array('serialization' => true, 'deserialization' => true))
+            ->treatFalseLike(array('serialization' => false, 'deserialization' => false))
+            ->addDefaultsIfNotSet()
+            ->children()
+                ->booleanNode('serialization')->defaultFalse()->end()
+                ->booleanNode('deserialization')->defaultFalse()->end()
+            ->end()
+        ;
+    }
+
+    public function getHandlerId(ContainerBuilder $container, array $config)
+    {
+        return 'jms_serializer.object_based_custom_handler';
+    }
+}

+ 18 - 0
DependencyInjection/HandlerDefinitionFactoryInterface.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace JMS\SerializerBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+
+interface HandlerDefinitionFactoryInterface
+{
+    const TYPE_SERIALIZATION   = 1;
+    const TYPE_DESERIALIZATION = 2;
+    const TYPE_ALL             = 3;
+
+    function getConfigKey();
+    function addConfiguration(ArrayNodeDefinition $builder);
+    function getType(array $config);
+    function getHandlerId(ContainerBuilder $container, array $config);
+}

+ 49 - 33
DependencyInjection/JMSSerializerExtension.php

@@ -18,6 +18,7 @@
 
 namespace JMS\SerializerBundle\DependencyInjection;
 
+use JMS\SerializerBundle\SerializerBundleAwareInterface;
 use Symfony\Component\HttpKernel\KernelInterface;
 use Symfony\Component\DependencyInjection\Alias;
 use Symfony\Component\DependencyInjection\DefinitionDecorator;
@@ -31,10 +32,26 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
 
 class JMSSerializerExtension extends Extension
 {
+    private $kernel;
+    private $factories = array();
+
+    public function __construct(KernelInterface $kernel)
+    {
+        $this->kernel = $kernel;
+    }
+
+    public function addHandlerDefinitionFactory(HandlerDefinitionFactoryInterface $factory)
+    {
+        $this->factories[$factory->getConfigKey()] = $factory;
+    }
+
     public function load(array $configs, ContainerBuilder $container)
     {
-        $config = $this->mergeConfigs($configs, $container->getParameter('kernel.debug'));
-        $loader = new XmlFileLoader($container, new FileLocator(array(__DIR__.'/../Resources/config/')));
+        $config = $this->processConfiguration($this->buildConfiguration(),
+                      $configs);
+
+        $loader = new XmlFileLoader($container, new FileLocator(array(
+                        __DIR__.'/../Resources/config/')));
         $loader->load('services.xml');
 
         // property naming
@@ -51,38 +68,32 @@ class JMSSerializerExtension extends Extension
             $container->setAlias('jms_serializer.naming_strategy', 'jms_serializer.cache_naming_strategy');
         }
 
-        // object based custom handler
-        if ($config['handlers']['object_based']['serialization']) {
-            $container->getDefinition('jms_serializer.object_based_custom_handler')->addTag('jms_serializer.serialization_handler');
-        }
-        if ($config['handlers']['object_based']['deserialization']) {
-            $container->getDefinition('jms_serializer.object_based_custom_handler')->addTag('jms_serializer.deserialization_handler');
-        }
+        // gather handlers
+        $serializationHandlers = $deserializationHandlers = array();
+        foreach ($config['handlers'] as $k => $handlerConfig) {
+            $id = $this->factories[$k]->getHandlerId($container, $handlerConfig);
+            $type = $this->factories[$k]->getType($handlerConfig);
 
-        // datetime handler
-        if (isset($config['handlers']['datetime'])) {
-            $container
-                ->getDefinition('jms_serializer.datetime_handler')
-                ->addArgument($config['handlers']['datetime']['format'])
-                ->addArgument($config['handlers']['datetime']['default_timezone'])
-            ;
-        } else {
-            $container->removeDefinition('jms_serializer.datetime_handler');
-        }
+            if (0 !== $type & HandlerDefinitionFactoryInterface::TYPE_SERIALIZATION) {
+                $serializationHandlers[] = new Reference($id);
+            }
 
-        // array collection handler
-        if (!$config['handlers']['array_collection']) {
-            $container->removeDefinition('jms_serializer.array_collection_handler');
+            if (0 !== $type & HandlerDefinitionFactoryInterface::TYPE_DESERIALIZATION) {
+                $deserializationHandlers[] = new Reference($id);
+            }
         }
 
-        // form error handler
-        if (!$config['handlers']['form_error']) {
-            $container->removeDefinition('jms_serializer.form_error_handler');
+        foreach (array('json', 'xml', 'yaml') as $format) {
+            $container
+                ->getDefinition('jms_serializer.'.$format.'_serialization_visitor')
+                ->replaceArgument(1, $serializationHandlers)
+            ;
         }
-
-        // constraint violation handler
-        if (!$config['handlers']['constraint_violation']) {
-            $container->removeDefinition('jms_serializer.constraint_violation_handler');
+        foreach (array('json', 'xml') as $format) {
+            $container
+                ->getDefinition('jms_serializer.'.$format.'_deserialization_visitor')
+                ->replaceArgument(1, $deserializationHandlers)
+            ;
         }
 
         // metadata
@@ -140,11 +151,16 @@ class JMSSerializerExtension extends Extension
         ;
     }
 
-    private function mergeConfigs(array $configs, $debug)
+    private function buildConfiguration()
     {
-        $processor = new Processor();
-        $config = new Configuration($debug);
+        foreach ($this->kernel->getBundles() as $bundle) {
+            if (!method_exists($bundle, 'configureSerializerExtension')) {
+                continue;
+            }
+
+            $bundle->configureSerializerExtension($this);
+        }
 
-        return $processor->process($config->getConfigTreeBuilder()->buildTree(), $configs);
+        return new Configuration($this->kernel->isDebug(), $this->factories);
     }
 }

+ 28 - 2
JMSSerializerBundle.php

@@ -18,16 +18,42 @@
 
 namespace JMS\SerializerBundle;
 
-use JMS\SerializerBundle\DependencyInjection\Compiler\SetCustomHandlersPass;
+use JMS\SerializerBundle\DependencyInjection\Factory\FormErrorFactory;
+use JMS\SerializerBundle\DependencyInjection\Factory\DateTimeFactory;
+use JMS\SerializerBundle\DependencyInjection\Factory\ConstraintViolationFactory;
+use JMS\SerializerBundle\DependencyInjection\Factory\ArrayCollectionFactory;
+use JMS\SerializerBundle\DependencyInjection\Factory\ObjectBasedFactory;
+use JMS\SerializerBundle\DependencyInjection\JMSSerializerExtension;
+use Symfony\Component\HttpKernel\KernelInterface;
 use JMS\SerializerBundle\DependencyInjection\Compiler\SetVisitorsPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 
 class JMSSerializerBundle extends Bundle
 {
+    private $kernel;
+
+    public function __construct(KernelInterface $kernel)
+    {
+        $this->kernel = $kernel;
+    }
+
+    public function getContainerExtension()
+    {
+        return new JMSSerializerExtension($this->kernel);
+    }
+
+    public function configureSerializerExtension(JMSSerializerExtension $ext)
+    {
+        $ext->addHandlerDefinitionFactory(new ObjectBasedFactory());
+        $ext->addHandlerDefinitionFactory(new ArrayCollectionFactory());
+        $ext->addHandlerDefinitionFactory(new ConstraintViolationFactory());
+        $ext->addHandlerDefinitionFactory(new DateTimeFactory());
+        $ext->addHandlerDefinitionFactory(new FormErrorFactory());
+    }
+
     public function build(ContainerBuilder $builder)
     {
         $builder->addCompilerPass(new SetVisitorsPass());
-        $builder->addCompilerPass(new SetCustomHandlersPass());
     }
 }

+ 9 - 11
Resources/config/services.xml

@@ -30,6 +30,7 @@
         <parameter key="jms_serializer.json_deserialization_visitor.class">JMS\SerializerBundle\Serializer\JsonDeserializationVisitor</parameter>
         <parameter key="jms_serializer.xml_serialization_visitor.class">JMS\SerializerBundle\Serializer\XmlSerializationVisitor</parameter>
         <parameter key="jms_serializer.xml_deserialization_visitor.class">JMS\SerializerBundle\Serializer\XmlDeserializationVisitor</parameter>
+        <parameter key="jms_serializer.yaml_serialization_visitor.class">JMS\SerializerBundle\Serializer\YamlSerializationVisitor</parameter>
         
         <parameter key="jms_serializer.object_based_custom_handler.class">JMS\SerializerBundle\Serializer\Handler\ObjectBasedCustomHandler</parameter>
         <parameter key="jms_serializer.datetime_handler.class">JMS\SerializerBundle\Serializer\Handler\DateTimeHandler</parameter>
@@ -136,25 +137,22 @@
             <argument type="service" id="jms_serializer.object_constructor" />
             <tag name="jms_serializer.deserialization_visitor" format="xml" />
         </service>
+        <service id="jms_serializer.yaml_serialization_visitor" class="%jms_serializer.yaml_serialization_visitor.class%" public="false">
+            <argument type="service" id="jms_serializer.naming_strategy" />
+            <argument type="collection" /><!-- Custom Handlers -->
+            <tag name="jms_serializer.serialization_visitor" format="yml" />
+        </service>
         
         <!-- Custom Handlers -->
         <service id="jms_serializer.object_based_custom_handler" class="%jms_serializer.object_based_custom_handler.class%" public="false">
             <argument type="service" id="jms_serializer.unserialize_object_constructor" />
             <argument type="service" id="jms_serializer.metadata_factory" />
         </service>
-        <service id="jms_serializer.datetime_handler" class="%jms_serializer.datetime_handler.class%" public="false">
-            <tag name="jms_serializer.serialization_handler" />
-            <tag name="jms_serializer.deserialization_handler" />
-        </service>
-        <service id="jms_serializer.array_collection_handler" class="%jms_serializer.array_collection_handler.class%" public="false">
-            <tag name="jms_serializer.deserialization_handler" />
-        </service>
+        <service id="jms_serializer.datetime_handler" class="%jms_serializer.datetime_handler.class%" public="false" />
+        <service id="jms_serializer.array_collection_handler" class="%jms_serializer.array_collection_handler.class%" public="false" />
         <service id="jms_serializer.form_error_handler" class="%jms_serializer.form_error_handler.class%" public="false">
             <argument type="service" id="translator" />
-            <tag name="jms_serializer.serialization_handler" />
-        </service>
-        <service id="jms_serializer.constraint_violation_handler" class="%jms_serializer.constraint_violation_handler.class%" public="false">
-            <tag name="jms_serializer.serialization_handler" />
         </service>
+        <service id="jms_serializer.constraint_violation_handler" class="%jms_serializer.constraint_violation_handler.class%" public="false" />
     </services>
 </container>

+ 80 - 0
Resources/doc/extending.rst

@@ -0,0 +1,80 @@
+Extending The Serializer
+========================
+
+This document details the different extension points, and how you can utilize
+them to change the default behavior of the serializer.
+
+Custom De-/Serialization Handlers
+---------------------------------
+This allows you to change the way of how a specifc type is being de-/serialized.
+
+Any handler must implement either the ``SerializationHandlerInterface``, or
+``DeserializationHandlerInterface``, or both. This bundle already comes with
+some handlers which you find in the Serializer/Handler folder, and which you
+can use as a starting point.
+
+Custom handlers are normal services, and thus can have dependencies on any
+other service available in the dependency injection container.
+
+After you have written your handler, you can write a service definition. Such
+as the following::
+
+    <service id="acme_foo.serializer.my_handler"
+             class="Acme\FooBundle\Serializer\MyHandler"
+             public="false"
+             abstract="true" />
+             
+Note that we have declared this definition abstract, we will later see
+why. At the moment, do not worry too much about this. 
+
+What is left to do is to publish our new handler to this bundle. So it gets
+picked up, and wired with the correct serializer. In order to do this, this
+bundle uses a configuration system similar to that of the SecurityBundle. Each
+handler needs a corresponding factory::
+
+    <?php
+    
+    namespace Acme\FooBundle\DependencyInjection\Factory;
+    
+    use JMS\SerializerBundle\DependencyInjection\SerializerFactoryInterface;
+    
+    class MyHandlerFactory implements SerializerFactoryInterface
+    {
+        public function getKey()
+        {
+            return 'acme_foo_my';
+        }
+        
+        public function addConfiguration(ArrayNodeDefinition $builder)
+        {
+        
+        }
+        
+        public function process(ContainerBuilder $container, array $config, $id)
+        {
+        
+        }
+    }
+    
+This factory is responsible for setting up the configuration for your handler
+in the ``addConfiguration`` method, and then process that configuration in the
+``process`` method. 
+
+The last thing left to do, is to add this factory to this bundle. This is
+done by adding a ``configureSerializerExtension`` to your bundle class::
+
+    <?php
+    
+    namespace Acme\FooBundle;
+    
+    use Acme\FooBundle\DependencyInjection\Factory\FooFactory;
+    use JMS\SerializerBundle\DependencyInjection\JMSSerializerExtension;
+    use Symfony\Component\HttpKernel\Bundle\Bundle;
+    
+    class AcmeFooBundle extends Bundle
+    {
+        public function configureSerializerExtension(JMSSerializerExtension $ext)
+        {
+            $ext->addFactory(new FooFactory());
+        }
+    }

+ 8 - 0
SerializerBundleAwareInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace JMS\SerializerBundle;
+
+interface SerializerBundleAwareInterface
+{
+    function configureSerializerExtension(JMSSerializerExtension $extension);
+}

+ 15 - 3
Tests/DependencyInjection/JMSSerializerExtensionTest.php

@@ -18,6 +18,8 @@
 
 namespace JMS\SerializerBundle\Tests\DependencyInjection;
 
+use Symfony\Component\HttpKernel\KernelInterface;
+
 use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
 
 use Symfony\Component\DependencyInjection\Compiler\RepeatedPass;
@@ -75,9 +77,20 @@ class JMSSerializerExtensionTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals(json_encode(array('name' => 'bar')), $serializer->serialize($versionedObject, 'json'));
     }
 
-    private function getContainerForConfig(array $configs)
+    private function getContainerForConfig(array $configs, KernelInterface $kernel = null)
     {
-        $extension = new JMSSerializerExtension();
+        if (null === $kernel) {
+            $kernel = $this->getMock('Symfony\Component\HttpKernel\KernelInterface');
+            $kernel
+                ->expects($this->any())
+                ->method('getBundles')
+                ->will($this->returnValue(array()))
+            ;
+        }
+
+        $bundle = new JMSSerializerBundle($kernel);
+        $extension = $bundle->getContainerExtension();
+
         $container = new ContainerBuilder();
         $container->setParameter('kernel.debug', true);
         $container->setParameter('kernel.cache_dir', sys_get_temp_dir());
@@ -87,7 +100,6 @@ class JMSSerializerExtensionTest extends \PHPUnit_Framework_TestCase
         $container->set('translator', $this->getMock('Symfony\\Component\\Translation\\TranslatorInterface'));
         $extension->load($configs, $container);
 
-        $bundle = new JMSSerializerBundle();
         $bundle->build($container);
 
         $container->getCompilerPassConfig()->setOptimizationPasses(array(