You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为JMS Serializer添加自定义注解?是否需重写AnnotationDriver?

Adding Custom Annotations to JMS Serializer: Approaches & AnnotationDriver Rewrite Considerations

Hey Patrick, great question! Let’s walk through how to add custom annotations to JMS Serializer, and whether rewriting the AnnotationDriver makes sense.

First: Is Rewriting AnnotationDriver the Right Move?

Short answer: It’s usually not the first choice, but it’s reasonable if you have very specific needs that can’t be met with extension patterns. Let’s start with the more maintainable approaches first, then cover when rewriting makes sense.

Approach 1: Decorate the AnnotationDriver (Preferred)

Since the AnnotationDriver can’t be extended directly, the decorator pattern is a clean way to add custom annotation handling without modifying the original class. Here’s how to implement it:

Step 1: Define Your Custom Annotation

First, create your annotation using Doctrine’s annotation library (JMS Serializer relies on this under the hood):

use Doctrine\Common\Annotations\Annotation;

/**
 * @Annotation
 * @Target({"PROPERTY", "CLASS"}) // Adjust target based on your needs
 */
class MyCustomAnnotation
{
    // Add any properties your annotation needs
    public string $customValue;
}

Step 2: Build a Decorator Driver

Create a new driver that wraps the original AnnotationDriver, adds your custom annotation processing, and implements MetadataDriverInterface:

use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\Driver\AnnotationDriver;
use JMS\Serializer\Metadata\Driver\MetadataDriverInterface;
use Doctrine\Common\Annotations\Reader;
use ReflectionClass;

class CustomAnnotationDecorator implements MetadataDriverInterface
{
    private AnnotationDriver $originalDriver;
    private Reader $annotationReader;

    public function __construct(AnnotationDriver $originalDriver, Reader $annotationReader)
    {
        $this->originalDriver = $originalDriver;
        $this->annotationReader = $annotationReader;
    }

    public function loadMetadataForClass(ReflectionClass $class): ClassMetadata
    {
        // Let the original driver load all default JMS annotations first
        $metadata = $this->originalDriver->loadMetadataForClass($class);

        // Process class-level custom annotations
        $classAnnotation = $this->annotationReader->getClassAnnotation($class, MyCustomAnnotation::class);
        if ($classAnnotation instanceof MyCustomAnnotation) {
            // Add your custom logic here—e.g., modify class metadata
            // Example: $metadata->setCustomAttribute('my_custom_data', $classAnnotation->customValue);
        }

        // Process property-level custom annotations
        foreach ($class->getProperties() as $property) {
            $propAnnotation = $this->annotationReader->getPropertyAnnotation($property, MyCustomAnnotation::class);
            if ($propAnnotation instanceof MyCustomAnnotation) {
                $propMetadata = $metadata->getPropertyMetadata($property->getName());
                if ($propMetadata) {
                    // Modify property metadata based on your annotation
                    // Example: $propMetadata->serializerCallback = [$this, 'customSerializeCallback'];
                }
            }
        }

        return $metadata;
    }

    // Delegate other interface methods to the original driver
    public function getAllClassNames(): array
    {
        return $this->originalDriver->getAllClassNames();
    }

    public function isTransient(ReflectionClass $class): bool
    {
        return $this->originalDriver->isTransient($class);
    }
}

Step 3: Configure the Serializer to Use Your Decorator

Swap out the original driver with your decorator when building the serializer:

use JMS\Serializer\SerializerBuilder;
use Doctrine\Common\Annotations\AnnotationReader;

$annotationReader = new AnnotationReader();
$originalDriver = new AnnotationDriver($annotationReader);
$customDriver = new CustomAnnotationDecorator($originalDriver, $annotationReader);

$serializer = SerializerBuilder::create()
    ->setMetadataDriver($customDriver)
    ->build();

Approach 2: Rewrite AnnotationDriver (When Necessary)

Rewriting the entire AnnotationDriver is reasonable only if you need to completely overhaul how annotations are parsed—for example, replacing the Doctrine annotation reader with a different system, or changing core logic for how JMS annotations are processed.

If you go this route:

  1. Implement MetadataDriverInterface in your custom driver.
  2. Copy the core logic from the original AnnotationDriver (handling built-in JMS annotations, reflection, etc.).
  3. Insert your custom annotation handling alongside the existing logic.

Caveats to Keep in Mind:

  • Maintenance Overhead: You’ll need to sync your code with any updates to the original AnnotationDriver—if JMS Serializer changes how it parses annotations, your custom driver could break.
  • Version Locking: Consider pinning your JMS Serializer version to avoid unexpected changes breaking your implementation.

Final Recommendations

  • Start with the decorator pattern: It’s cleaner, follows the open/closed principle, and avoids duplicating code from the original driver.
  • Only rewrite AnnotationDriver if you have no other option: Reserve this for cases where decorators can’t address your specific requirements (e.g., replacing the entire annotation parsing pipeline).

内容的提问来源于stack exchange,提问作者Patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:50:00