Symfony Serializer 的错误无法将 int 标准化为 float

问题描述 投票:0回答:2

当我尝试

denormalize
具有
float
属性但具有
int
值的对象时,出现错误。

Symfony\Component\Serializer\Exception\NotNormalizableValueException

类的“solde”属性的类型 “钱包”必须是“float”之一(“int” 给出)。

我的序列化器配置:

class SerializerService implements SerializerInterface, NormalizerInterface, DenormalizerInterface
{
    public const JSON_FORMAT = 'json';

    /**
     * @var Serializer
     */
    private $serializer;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;

        $this->serializer = new Serializer([
            new DateTimeNormalizer(),
            new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
            new ArrayDenormalizer()
        ], [
            new JsonEncoder(
                new JsonEncode(),
                new JsonDecode(),
            )
        ]);
    }
...
...

我在 Symfony Serializer 文档中没有找到任何相关内容。

你能帮我吗?

php symfony serialization denormalization
2个回答
0
投票

问题不在于序列化器,而在于您的数据。你可能有这样的想法:

{
  “solde”: 1.0
}

但它应该是:

{“solde”: 1}
,即您的数据应该是 int 而不是 float。

如果你将 Wallet 对象中的

$solde
设置为 float,而不是 int,则不会出现此问题,因为从 int 转换为 float 通常是无损的,即 1 到 1.0。另外,您一开始就不应该接受此类数据的浮点数。假设“1.0”代表 1 美元或 1 欧元,那么通常将其保存为字符串或整数,即 100,我建议您的 JSON 也使用这种格式。这样,您就不需要浮点数,也不会遇到奇怪的浮点问题。我强烈建议走这条路。由于您以另一种方式进行转换(从 float 到 int),您将失去精度,即 1.5 将变为 1 或 2,这显然很糟糕,因为您的用户可能不知道这将是 2 个可能结果中的哪一个。

回到你的错误。在您的对象中,您可以在接收数据(即从 JSON 设置字段)时或在读取数据时解决此类型问题。对于前者,在该字段的对象设置器内进行有损转换(转换为 int、舍入等)。您可能需要调整您的 ObjectNormalizer 以使用 getter 和 setter 来实现此目的,但默认情况下它应该使用 PropertyAccess 组件,如果我没有记错的话,它应该在查找属性之前使用 setter。大概是这样的:

class Wallet
{
    private int $solde;

    // …

    public function setSolde(float|int $solde): void
    {
        $this->solde = (int) $solde; // casting to int will just cut off anything after the “.” in floats
    }
}

您还可以让字段为 float 或 int,然后在 getter 上进行转换,即当您使用该字段时,但当您想要将数据保留在数据库中时可能会遇到问题。

class Wallet
{
    private int|float $solde;

    // …

    public function getSolde(): int
    {
        return (int) $this->solde; // casting to int will just cut off anything after the “.” in floats
    }
}

如果你不想为此调整你的对象,那么你可以通过编写自定义反规范化器来教序列化器进行有损转换(Symfony 在其规范化器中结合了反规范化器和规范化器,即 ObjectNormalizer,如果你正在寻找参考如何写)。然后,该反规范化器将处理您的对象(即 Wallet),并手动将 JSON 中的每个字段映射到对象的字段,包括“solde”,其中存在类型错误。然后,您可以通过执行有损转换(转换为 int、舍入等)来解决 Denormalizer 中的类型错误。您必须小心,不要使反规范化器过于通用,因为即使在您不希望这样做的地方,您最终也可能会这样做。 Symfony 在自定义规范化器/反规范化器的文档中有一部分,所以它应该相当简单。如果你真的需要这个,我建议你走这条路,假设你只需要在从 JSON 读取数据时担心这个问题。


0
投票

如果您传递“json”格式,Symfony Serialiser 无法更改值的类型。您应该严格遵循类型。

但是如果使用“xml”格式而不是 json,Symfony\Component\Serializer\NormalizerAbstractObjectNormalizer 中的“validateAndDenormalize”方法可以将类型从“string”更改为“int”或“float”。

© www.soinside.com 2019 - 2024. All rights reserved.