我正在尝试使用JMS Serializer序列化实体关系。
这是实体:
class Ad
{
/**
* @Type("string")
* @Groups({"manage"})
*
* @var string
*/
private $description;
/**
* @Type("Acme\SearchBundle\Entity\Country")
* @Groups({"manage"})
*
* @var \Acme\SearchBundle\Entity\Country
*/
private $country;
/**
* @Type("string")
* @Groups({"manage"})
*
* @var string
*/
private $title;
/**
* Set description
*
* @param string $description
* @return Ad
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set country
*
* @param \Acme\SearchBundle\Entity\Country $country
* @return Ad
*/
public function setCountry($country)
{
$this->country= $country;
return $this;
}
/**
* Get country
*
* @return string
*/
public function getCountry()
{
return $this->country;
}
/**
* Set title
*
* @param string $title
* @return Ad
*/
public function setTituloanuncio($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
}
和关系的实体:
class Country
{
/**
* @Type("string")
* @Groups("manage")
*
* @var string
*/
private $id;
/**
* @Type("string")
* @Groups("admin")
*
* @var string
*/
private $description;
/**
* Set description
* @Groups("")
*
* @param string $description
* @return Country
*/
public function setDescripcionpais($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
}
/**
* Get id
*
* @return string
*/
public function getId()
{
return $this->id;
}
}
我序列化实体但我不知道如何将country属性转换为简单字段。
我在json中得到了这个结果:
{"description":"foo", "title":"bar", "country":{"id":"en"} }
但我想得到这样的国家的id字段:
{"description":"foo", "title":"bar", "country": "en" }
有可能使用JMS Serializer吗?
谢谢。
[编辑]
@VirtualProperty不起作用。
是的,您可以使用@VirtualProperty
注释:
/**
* @VirtualProperty
* @SerializedName("foo")
*/
public function bar()
{
return $this->country->getCode();
}
但是请注意反序列化:
@VirtualProperty可以在方法上定义此批注,以指示方法返回的数据应该看起来像对象的属性。
>注意:这仅适用于序列化,在反序列化期间完全被忽略。
希望这可以帮助...
只是为了回答问题:
如果你不喜欢为每个关系编写一个方法 - 只需编写自己的处理程序。这很容易
final class RelationsHandler
{
/**
* @var EntityManagerInterface
*/
private $manager;
/**
* RelationsHandler constructor.
*
* @param EntityManagerInterface $manager
*/
public function __construct(EntityManagerInterface $manager) { $this->manager = $manager; }
public function serializeRelation(JsonSerializationVisitor $visitor, $relation, array $type, Context $context)
{
if ($relation instanceof \Traversable) {
$relation = iterator_to_array($relation);
}
if (is_array($relation)) {
return array_map([$this, 'getSingleEntityRelation'], $relation);
}
return $this->getSingleEntityRelation($relation);
}
/**
* @param $relation
*
* @return array|mixed
*/
protected function getSingleEntityRelation($relation)
{
$metadata = $this->manager->getClassMetadata(get_class($relation));
$ids = $metadata->getIdentifierValues($relation);
if (!$metadata->isIdentifierComposite) {
$ids = array_shift($ids);
}
return $ids;
}
}
注册处理程序
jms_serializer.handler.relation:
class: MyBundle\RelationsHandler
arguments:
- "@doctrine.orm.entity_manager"
tags:
- { name: jms_serializer.handler, type: Relation, direction: serialization, format: json, method: serializeRelation}
- { name: jms_serializer.handler, type: Relation, direction: deserialization, format: json, method: deserializeRelation}
- { name: jms_serializer.handler, type: Relation<?>, direction: serialization, format: json, method: serializeRelation}
- { name: jms_serializer.handler, type: Relation<?>, direction: deserialization, format: json, method: deserializeRelation}
这允许您用`Type(“Relation”)替换虚拟getter方法。
如果你也不想反序列化关系 - 你应该告诉每个@Type("Relation")
它应该反序列化的类名(@Type("Relation<FQCN>")
)或者将元数据驱动程序包装成为你做的。
public function deserializeRelation(JsonDeserializationVisitor $visitor, $relation, array $type, Context $context)
{
$className = isset($type['params'][0]['name']) ? $type['params'][0]['name'] : null;
if (!class_exists($className, false)) {
throw new \InvalidArgumentException('Class name should be explicitly set for deserialization');
}
$metadata = $this->manager->getClassMetadata($className);
if (!is_array($relation)) {
return $this->manager->getReference($className, $relation);
}
$single = false;
if ($metadata->isIdentifierComposite) {
$single = true;
foreach ($metadata->getIdentifierFieldNames() as $idName) {
$single = $single && array_key_exists($idName, $relation);
}
}
if ($single) {
return $this->manager->getReference($className, $relation);
}
$objects = [];
foreach ($relation as $idSet) {
$objects[] = $this->manager->getReference($className, $idSet);
}
return $objects;
}
我知道这已经得到了解答,但您也可以使用@Accessor。这可能(可能,我不能确定)也可以用于反序列化。
/**
* @Type("Acme\SearchBundle\Entity\Country")
* @Groups({"manage"})
*
* @var \Acme\SearchBundle\Entity\Country
*
* @Serializer\Accessor(getter="getCountryMinusId",setter="setCountryWithId")
*/
private $country;
/**
* @return string|null
*/
public function getCountryMinusId()
{
if (is_array($this->country) && isset($this->country['id'])) {
return $this->country['id'];
}
return null;
}
/**
* @param string $country
* @return $this
*/
public function setCountryWithId($country)
{
if (!is_array($this->country)) {
$this->country = array();
)
$this->country['id'] = $country;
return $this;
}
你可以使用@Type
和@Accessor
注释:
/**
* @Type("string")
* @Accessor(getter="serializeType",setter="setType")
*/
protected $type;
public function serializeType()
{
return $this->type->getId();
}
作者希望保留属性名称,该名称不适用于接受的答案。据我所知,ScayTrase的答案将保留原始属性名称,但根据评论还有另一个缺点:如果您使用Doctrine ORM @ManyToOne
,则会获取相关对象,从而降低性能。
如果要保留原始属性名称,则必须在类级别定义@VirtualProperty
,并在@Exclude
中定义原始属性。否则,序列化属性名称将从getter方法派生(在本例中为countryId
):
/**
* @Serializer\VirtualProperty(
* "country",
* exp="object.getCountryId()",
* options={@Serializer\SerializedName("country")}
* )
*/
class Ad {
/**
* @Serializer\Exclude
*/
private $country;
public function getCountryId() {
return $this->country === null ? null : $this->country->getId();
}
}
或者,你可以@inline
$country
将其属性序列化为父关系。然后你可以@Expose
国家$id
并将其@SerializedName
设置为"country"
。与虚拟属性不同,序列化和反序列化都适用于内联属性。
为了实现这一目标,您需要在每个类中使用@ExclusionPolicy("All")
,并明智地@Expose
您在任何组中需要的属性。 This is a more secure policy anyways。
/**
* @ExclusionPolicy("All")
*/
class Ad
{
//...
/**
* @Type("Acme\SearchBundle\Entity\Country")
*
* @Expose()
* @Inline()
* @Groups({"manage"})
*
* @var \Acme\SearchBundle\Entity\Country
*/
private $country;
//...
}
/**
* @ExclusionPolicy("All")
*/
class Country
{
//...
/**
* Get id
*
* @Expose()
* @Groups({"manage"})
* @SerializedName("country")
* @return string
*/
public function getId()
{
return $this->id;
}
}