当前位置: 首页 > 知识库问答 >
问题:

SQLAlchemy中json列的自定义json序列化程序

黎腾
2023-03-14

我有以下ORM对象(简化):

import datetime as dt

from sqlalchemy import create_engine, Integer, Column, DateTime
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Session, declarative_base

Base = declarative_base()

class Metrics(Base):
    __tablename__ = 'metrics'

    id = Column(Integer, primary_key=True)
    ts = Column(DateTime, default=dt.datetime.now())
    computed_values = Column(JSONB)
    dates = Column(JSONB)

    calculated_values = 
    entry = Metrics(computed_values={'foo': 12.3, 'bar':45.6},
                    dates=[datetime.date.today()])

    engine = create_engine('postgresql://postgres:postgres@localhost:5432/my_schema')
    with Session(engine, future=True) as session:
        session.add(entry)
        session.commit()

每行有:

  • id主键
  • ts插入行时的时间戳
  • computed_values要存储的实际JSONB数据
  • 日期JSONB来存储计算数据的日期列表。

虽然我对computed_values列没有问题,但是datetime。默认情况下,dates列中列表中的date对象不能被SQLAlchemy JSON序列化程序序列化。

我的想法是为该列重新定义date对象的序列化程序行为。要做到这一点,我必须定义自己的自定义JSON序列化程序,或者使用一些现成的序列化程序,比如orjson。因为我可能会在项目中遇到许多其他JSON序列化问题,所以我更喜欢后者。

深入研究JSONB类和它的超类,我认为下面应该可以做到这一点:

class Metrics(Base):
    __tablename__ = 'metrics'

    # ---%<--- snip ---%<---
    dates = Column(JSONB(json_serializer=lambda obj: orjson.dumps(obj, option=orjson.OPT_NAIVE_UTC)))

    # ---%<--- snip ---%<---

但事实并非如此:

File "metrics.py", line 30, in Metrics
    dates = Column(JSONB(json_serializer=lambda obj: orjson.dumps(obj, option=orjson.OPT_NAIVE_UTC)))
TypeError: __init__() got an unexpected keyword argument 'json_serializer'

我做错了什么,以及如何为JSON(和JSONB)列正确定义自定义SQLAlchemy序列化程序?

共有1个答案

韶亮
2023-03-14

看起来你应该能够通过修改你的create_engine语句得到你想要的。

来自SQLAlchemy中的文档字符串:

Custom serializers and deserializers are specified at the dialect level,
that is using :func:`_sa.create_engine`.  The reason for this is that when
using psycopg2, the DBAPI only allows serializers at the per-cursor
or per-connection level.   E.g.::
    engine = create_engine("postgresql://scott:tiger@localhost/test",
                            json_serializer=my_serialize_fn,
                            json_deserializer=my_deserialize_fn
                    )

因此,生成的代码应如下所示:

import datetime as dt

import orjson

from sqlalchemy import create_engine, Integer, Column, DateTime
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Session, declarative_base

Base = declarative_base()

class Metrics(Base):
    __tablename__ = 'metrics'

    id = Column(Integer, primary_key=True)
    ts = Column(DateTime, default=dt.datetime.now())
    computed_values = Column(JSONB)
    dates = Column(JSONB)

    calculated_values = 
    entry = Metrics(computed_values={'foo': 12.3, 'bar':45.6},
                    dates=[datetime.date.today()])

    def orjson_serializer(obj):
        """
            Note that `orjson.dumps()` return byte array, while sqlalchemy expects string, thus `decode()` call.
        """
        return orjson.dumps(obj, option=orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NAIVE_UTC).decode()
    

    engine = create_engine('postgresql://postgres:postgres@localhost:5432/my_schema', json_serializer=orjson_serializer, json_deserializer=orjson.loads)
    with Session(engine, future=True) as session:
        session.add(entry)
        session.commit()
 类似资料:
  • 问题内容: 有没有一种方法/库可以让我像GSON自定义序列化程序一样自定义JSON序列化? 这是我想要得到的: 此对象: KeyValuePair(“ Age”,10)myAge 通常会像 “ myAge”:{“ Key”:“ Age”,“ Value”:10} 同时我希望它进行序列化,例如: “年龄”:10 。有任何想法吗? 问题答案: 首先,我建议使用newton json dll。 其次,您

  • 问题内容: 我的代码结构如下。 和 基本上我正在扫描网站以获取统计信息,例如标题标签,重复标题等。 我正在使用JQuery并向Web服务进行AJAX调用并检索url统计信息,而该过程正在运行以显示到目前为止收集的用户url统计信息,因为扫描大型网站需要花费大量时间。因此,每隔5秒,我就会从服务器检索统计信息。现在的问题是我需要在扫描处理完成时(而不是更新期间)最后发送所有List变量数据。现在发生

  • 问题内容: 我用来将一些对象作为JSON值返回,并且除了一个字段外,它都工作正常。有没有一种简单的方法可以在一个字段中添加内容而无需手动创建整个json模板? 问题答案: Play使用GSON构建JSON字符串。如果您的字段是一种特定的对象类型,则可以通过为该类型提供自定义的序列化来轻松完成此操作。在这里查看文档 http://sites.google.com/site/gson/gson-use

  • 我想以 json 格式序列化一个自定义对象,其中 entryData 是我的域对象的列表。像这样: 下面是我在一次尝试中为获得json输出所做的工作: 但结果是entryData评估为字符串,引号转义: 我也尝试过这样做: 但是我得到了这个例外:

  • 问题内容: 我正在使用Flickr API 。调用该方法时,默认的JSON结果为: 我想将此响应解析为Java对象: JSON属性应按以下方式映射: 不幸的是,我无法找到一种使用Annotations做到这一点的好方法。到目前为止,我的方法是将JSON字符串读入a 并从中获取值。 但是我认为,这是有史以来最不优雅的方式。有没有简单的方法,可以使用注释还是自定义反序列化器? 这对我来说将是很明显的,

  • 有没有一种方法可以使用Jackson JSON处理器来执行自定义字段级别的序列化?例如,我想让 注意,age=25被编码为数字,而favoritenumber=123被编码为字符串。Jackson将编组为一个数字。在这种情况下,我希望将favoriteNumber编码为字符串。