Encode decimals as number in JSON

This commit is contained in:
Oliver Traber 2025-04-28 17:52:35 +00:00
parent 148275dd98
commit b1a94c5359
Signed by: Bluemedia
GPG key ID: C0674B105057136C
6 changed files with 36 additions and 1 deletions

View file

@ -8,6 +8,8 @@ from app.schemas.connector import Connector
from ocpp.v201.enums import ResetEnumType, ResetStatusEnumType from ocpp.v201.enums import ResetEnumType, ResetStatusEnumType
from app.util.encoders import decimal_encoder
class ChargePointBase(BaseModel): class ChargePointBase(BaseModel):
identity: str identity: str
is_active: bool is_active: bool
@ -32,6 +34,7 @@ class ChargePoint(ChargePointBase):
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = {Decimal: decimal_encoder}
class ChargePointPassword(BaseModel): class ChargePointPassword(BaseModel):
password: str password: str

View file

@ -4,6 +4,8 @@ from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
import enum import enum
from app.util.encoders import decimal_encoder
class AttributeType(enum.Enum): class AttributeType(enum.Enum):
ACTUAL = "Actual" ACTUAL = "Actual"
TARGET = "Target" TARGET = "Target"
@ -52,6 +54,7 @@ class ChargepointVariable(BaseModel):
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = {Decimal: decimal_encoder}
class ChargepointVariableUpdate(BaseModel): class ChargepointVariableUpdate(BaseModel):
value: str value: str

View file

@ -5,6 +5,8 @@ from typing import Optional
from uuid import UUID from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from app.util.encoders import decimal_encoder
class PhaseType(enum.Enum): class PhaseType(enum.Enum):
L1 = "L1" L1 = "L1"
L2 = "L2" L2 = "L2"
@ -55,3 +57,4 @@ class MeterValue(BaseModel):
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = {Decimal: decimal_encoder}

View file

@ -8,4 +8,5 @@ class Session(BaseModel):
name: str name: str
last_used: datetime last_used: datetime
model_config = {"from_attributes": True} class Config:
from_attributes = True

View file

@ -5,6 +5,8 @@ from typing import Optional
from uuid import UUID from uuid import UUID
from pydantic import BaseModel from pydantic import BaseModel
from app.util.encoders import decimal_encoder
class TransactionStatus(enum.Enum): class TransactionStatus(enum.Enum):
ONGOING = "ongoing" ONGOING = "ongoing"
ENDED = "ended" ENDED = "ended"
@ -50,6 +52,7 @@ class Transaction(BaseModel):
class Config: class Config:
from_attributes = True from_attributes = True
json_encoders = {Decimal: decimal_encoder}
class RemoteTransactionStartStopResponse(BaseModel): class RemoteTransactionStartStopResponse(BaseModel):
status: RemoteTransactionStartStopStatus status: RemoteTransactionStartStopStatus

View file

@ -0,0 +1,22 @@
from decimal import Decimal
from typing import Union
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
"""Encodes a Decimal as int of there's no exponent, otherwise float.
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
where a integer (but not int typed) is used. Encoding this as a float
results in failed round-tripping between encode and parse.
Our Id type is a prime example of this.
>>> decimal_encoder(Decimal("1.0"))
1.0
>>> decimal_encoder(Decimal("1"))
1
"""
exponent = dec_value.as_tuple().exponent
if isinstance(exponent, int) and exponent >= 0:
return int(dec_value)
else:
return float(dec_value)