JMSSerializer 分组属性的交集
JMSSerializer intersection of grouped properties
我有如下实体:
class A
{
/**
* @JMS\Groups({"writable", "other"})
*/
private $varA;
/**
* @JMS\Groups({"writable"})
*/
private $varB;
/**
* @JMS\Groups({"other"})
*/
private $varC;
}
我想让序列化器为两个组中都存在的属性生成输出,所以用更简单的话来说,我需要分组属性的交集。
$context = SerializationContext::create()->setGroups(['writable' ,'other']);
$serializer->serialize(new A(), 'json', $context);
上面的代码应该只输出变量 $varA
因为它定义了两个组。
如何实现?我唯一想到的是扩展来自 JMSSerializer 的 GroupExclusionStategy,但也许有更好的方法?
我仔细研究了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));
}
}
也许一个简单的解决方案是将另一个组名 ("exclusive") 添加到 $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
我在这里留下一个使用标志 "strict" 策略的实现:
$context = SerializationContext::create();
$context->addExclusionStrategy(
new CustomGroupsExclusionStrategy(['writable', 'other'], true)
);
$json = $this->get('serializer')->serialize(new A(), 'json', $context);
输出:
{"varA":"foo"}
我有如下实体:
class A
{
/**
* @JMS\Groups({"writable", "other"})
*/
private $varA;
/**
* @JMS\Groups({"writable"})
*/
private $varB;
/**
* @JMS\Groups({"other"})
*/
private $varC;
}
我想让序列化器为两个组中都存在的属性生成输出,所以用更简单的话来说,我需要分组属性的交集。
$context = SerializationContext::create()->setGroups(['writable' ,'other']);
$serializer->serialize(new A(), 'json', $context);
上面的代码应该只输出变量 $varA
因为它定义了两个组。
如何实现?我唯一想到的是扩展来自 JMSSerializer 的 GroupExclusionStategy,但也许有更好的方法?
我仔细研究了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));
}
}
也许一个简单的解决方案是将另一个组名 ("exclusive") 添加到 $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
我在这里留下一个使用标志 "strict" 策略的实现:
$context = SerializationContext::create();
$context->addExclusionStrategy(
new CustomGroupsExclusionStrategy(['writable', 'other'], true)
);
$json = $this->get('serializer')->serialize(new A(), 'json', $context);
输出:
{"varA":"foo"}