0

I have a class in Python called TransactionData:

from dataclasses import dataclass
from decimal import *


@dataclass(unsafe_hash=True)
class TransactionData:
    date: str
    transaction: str
    cheque_no: str
    debit: Decimal
    credit: Decimal
    balance: Decimal

    def __post_init__(self):
        if not isinstance(self.date, str):
            raise ValueError('date provided is not a string.')
        if not isinstance(self.transaction, str):
            raise ValueError('transaction provided is not a string.')
        if not isinstance(self.cheque_no, str):
            raise ValueError('cheque_no provided is not a string.')
        if not isinstance(self.debit, Decimal):
            raise ValueError('debit provided is not a Decimal.')
        if not isinstance(self.credit, Decimal):
            raise ValueError('credit provided is not a Decimal.')
        if not isinstance(self.balance, Decimal):
            raise ValueError('balance provided is not a Decimal.')

I want to serialize it to a Json with json.dumps(). I have extended JSONEncoder to be able to serialize Decimal:

import decimal
class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return str(o)
        return super(DecimalEncoder, self).default(o)

Now I try to serialize TransactionData:

transaction = TransactionData(
    '28 Aug','Cheque', '187264', Decimal('100.00'), Decimal('200.00'), Decimal('300.00'))

json.dumps(transaction, cls=DecimalEncoder)

I'm getting this error:

TypeError: Object of type TransactionData is not JSON serializable

I've heard you can use simplejson for this but I would still prefer to use the standard json library going forward. What is the solution to this?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
clattenburg cake
  • 1,096
  • 3
  • 19
  • 40
  • This looks very helpful @mkrieger1, but how do I serialize the `Decimal` in the dataclass then? I'm confused now... – clattenburg cake Aug 28 '22 at 22:23
  • 1
    Just as you did before, by converting it to `str`: `if isinstance(o, TransactionData): return {k: str(v) for k, v in dataclasses.asdict(o).items()}`, for example. – mkrieger1 Aug 28 '22 at 22:26
  • @mkrieger1 Oh thanks!!! One last thing though please... If I wanted the last three fields `debit`, `credit` and `balance` to come out as a number, what's the syntax? Currently they are all printing as strings. – clattenburg cake Aug 28 '22 at 22:32
  • 1
    Maybe convert them to a number instead of a string? https://stackoverflow.com/questions/32285927/how-to-do-decimal-to-float-conversion-in-python However if you use Decimal instead of float you *do* want strings to keep the precision. – mkrieger1 Aug 28 '22 at 22:34
  • I personally would use a (de)serialization tool like the [dataclass-wizard](https://dataclass-wizard.readthedocs.io/), and then you don't need to define a __post_init__() method. Also you can just do something like `TransactionData.from_dict({'debit': '1.2', ...})` and it should load the correct or desired types. – rv.kvetch Aug 29 '22 at 13:25

0 Answers0