msgspec icon indicating copy to clipboard operation
msgspec copied to clipboard

Union of str and decimal

Open LukasKrocek opened this issue 2 years ago • 3 comments

Description

Hello, I have a situation where I am getting updates from the server in msgpack format, one of the fields is value that can be Union of str, int, float, bool and newly decimal. Looking through releases I see that you added support for encoding/decoding decimals but Union of str encoded types is not supported.

Do you have any tip how to surpass that? I thought custom extension would work, but since decimal is one of supported types now, I am not able to write extension for it.

Thank you

LukasKrocek avatar Mar 22 '23 23:03 LukasKrocek

As a temporary solution only thing I could think of was to do something like this:

from typing import Any
from _decimal import Decimal as BuiltInDecimal

import msgspec


class Decimal(BuiltInDecimal):
    """Unfortunate hack to make msgspec serialize Decimal objects as extension type not as string."""

DECIMAL_TYPE_CODE = 17


def enc_hook(obj: Any) -> Any:
    if isinstance(obj, Decimal):
        encoded_dec = str(obj).encode("utf-8")
        data = struct.pack(f"{len(encoded_dec)}s", encoded_dec)
        # Return an `Ext` object so msgspec serializes it as an extension type.
        return msgspec.msgpack.Ext(DECIMAL_TYPE_CODE, data)

    # Raise a TypeError for other types
    raise TypeError(f"Objects of type {type(obj)} are not supported")


def ext_hook(code: int, data: memoryview) -> Any:
    if code == DECIMAL_TYPE_CODE:
        dec_string = struct.unpack(f"{data.nbytes}s", data)[0].decode("utf-8")
        return Decimal(dec_string)

    # Raise a TypeError for other extension type codes
    raise TypeError(f"Extension type code {code} is not supported") 

class A(msgspec.Struct, array_like=True):
    value: Any  # str | Decimal | int | float | bool | None
    
DECODER = msgspec.msgpack.Decoder(type=A, ext_hook=ext_hook)
ENCODER = msgspec.msgpack.Encoder(enc_hook=enc_hook)

LukasKrocek avatar Mar 23 '23 16:03 LukasKrocek

Just adding to this discussion, we like that msgspec handles decimal however they are embedded as strings. Here is some finding from another discussion around handling decimals.

https://github.com/meltano/sdk/issues/1046#issuecomment-1535493163

Do we think that msgspec might output decimals like simplejson without the double quotes in the future?

s7clarke10 avatar Jun 12 '23 01:06 s7clarke10

That's unrelated to this issue, I've opened a new issue for this at #440.

jcrist avatar Jun 12 '23 19:06 jcrist