JMSSerializer分组属性的交集

时间:2016-11-23 13:00:28

标签: php symfony jmsserializerbundle jms-serializer

我有以下实体:

class A
{
   /**
    * @JMS\Groups({"writable", "other"})
    */
   private $varA;

   /**
    * @JMS\Groups({"writable"})
    */
   private $varB;

   /**
    * @JMS\Groups({"other"})
    */
   private $varC;
}

我想让序列化器为BOTH组中存在的属性生成输出,所以在简单的单词中我需要一组分组属性。

$context = SerializationContext::create()->setGroups(['writable' ,'other']);
$serializer->serialize(new A(), 'json', $context);

上面的代码应该只输出变量$varA,因为它定义了两个组。

如何实现呢?我想到的唯一一件事就是扩展来自JMSSerializer的GroupExclusionStategy,但也许还有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

我已经深入了解jms的代码,我发现setGroups使用的是GroupsExclusionStrategy,但也有不同的策略和ExclusionStrategyInterface。所以我已将此界面实现到我自己的

<?php

namespace AppBundle\Jms\Serializer;

use JMS\Serializer\Context;
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;

/**
 * Class IntersectGroupsExclusionStrategy
 * @package AppBundle\Jms
 */
class IntersectGroupsExclusionStrategy implements ExclusionStrategyInterface
{
    /**
     * @var array
     */
    private $groups;

    /**
     * IntersectGroupsExclusionStrategy constructor.
     * @param array $groups
     */
    public function __construct(array $groups)
    {
        $this->setGroups($groups);
    }

    /**
     * {@inheritDoc}
     */
    public function shouldSkipProperty(PropertyMetadata $property, Context $navigatorContext)
    {
         if (is_array($this->groups) && is_array($property->groups)) {
            return !(!empty($this->groups) && array_intersect($this->groups, $property->groups) === $this->groups);
         }

         return false;
    }

    /**
     * Whether the class should be skipped.
     *
     * @param ClassMetadata $metadata
     *
     * @return boolean
     */
    public function shouldSkipClass(ClassMetadata $metadata, Context $context)
    {
        return false;
    }

    /**
     * @param array $groups
     * @return $this
     */
    public function setGroups(array $groups)
    {
        $this->groups = $groups;

        return $this;
    }
}

序列化而不是使用setGroups我已经使用

$intersectExclusionStrategy = new IntersectGroupsExclusionStrategy($groups);
$serializationContext = SerializationContext::create();
$serializationContext->addExclusionStrategy($intersectExclusionStrategy);

$groups保留值['writable' ,'other']的位置。

效果很好。

如果有人需要,我也为它创建了测试。

<?php
use AppBundle\Jms\Serializer\IntersectGroupsExclusionStrategy;

class IntersectGroupsExclusionStrategyTest extends PHPUnit_Framework_TestCase
{
    public function testShouldSkipPropertyGroups()
    {
        $intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);

        $propertyMetaData = $this->getMock('JMS\Serializer\Metadata\PropertyMetadata', [], [], '', false);
        $context = $this->getMock('JMS\Serializer\Context', [], [], '', false);

        $propertyMetaData->groups = ['group_a', 'group_b', 'group_c'];

        $this->assertNotTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));

        $propertyMetaData->groups = ['group_a', 'group_b'];

        $this->assertNotTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));

    }

    public function testShouldNotSkipPropertyGroups()
    {
        $intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);

        $propertyMetaData = $this->getMock('JMS\Serializer\Metadata\PropertyMetadata', [], [], '', false);
        $context = $this->getMock('JMS\Serializer\Context', [], [], '', false);

        $propertyMetaData->groups = ['group_a', 'group_c'];

        $this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));

        $propertyMetaData->groups = ['group_d', 'group_e'];

        $this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));

        $intersectExclusionStrategy = new IntersectGroupsExclusionStrategy([]);

        $this->assertTrue($intersectExclusionStrategy->shouldSkipProperty($propertyMetaData, $context));
    }

    public function testShouldSkipClassReturnsFalse()
    {
        $intersectExclusionStrategy = new IntersectGroupsExclusionStrategy(['group_a', 'group_b']);

        $classMetaData = $this->getMock('JMS\Serializer\Metadata\ClassMetadata', [], [], '', false);
        $context = $this->getMock('JMS\Serializer\Context', [], [], '', false);

        $this->assertFalse($intersectExclusionStrategy->shouldSkipClass($classMetaData, $context));
    }
} 

答案 1 :(得分:1)

也许一个简单的解决方案可能是向$varA属性添加另一个组名(“独占”):

/**
 * @JMS\Groups({"writable", "other", "exclusive"})
 */
private $varA;

接下来是:

$context = SerializationContext::create()->setGroups('exclusive');

但可能这只是一个用例示例。另一方面,应该创建一个CustomGroupsExclusionStrategy()

默认情况下GroupsExclusionStrategy()会检查是否有任何属性组包含在请求的组中:

private function shouldSkipUsingGroups(PropertyMetadata $property, $groups)
{
    foreach ($property->groups as $group) {
        if (in_array($group, $groups)) {
            return false;
        }
    }

    return true;
}

所以,要做到这一点,你需要改变这个:

private function shouldSkipUsingGroups(PropertyMetadata $property, $groups)
{
    foreach ($groups as $group) {
        if (!in_array($group, $property->groups)) {
            return true;
        }
    }

    return false;
}

因此,所有请求的组必须包含在属性组中。

解决方案:

  

https://gist.github.com/yceruto/90b1ac46c8e33d51ec21079725949f77

我在这里使用标志“严格”策略留下了一个实现:

$context = SerializationContext::create();
$context->addExclusionStrategy(
    new CustomGroupsExclusionStrategy(['writable', 'other'], true)
);

$json = $this->get('serializer')->serialize(new A(), 'json', $context);

输出:

{"varA":"foo"}