Rails jsonb - 将 jsonb 保存到 Postgresql 数据库时,防止 JSON 键重新排序

skp*_*rin 7 postgresql json ruby-on-rails jsonb ruby-on-rails-5

我有一个列amount_splits,我需要按照我指定的键顺序将我的 JSON 保存到其中。

jsonbJSON 键保存到数据库时,如何防止 Rails / Postgres自动对其进行排序?(用于创建或更新)

看起来它试图按字母顺序排序,但做得很差。

这是我要保存的内容:

{
    "str_fee": 3.17,       # key 1
    "eva_fee": 14.37,      # key 2
    "fran_royalty": 14.37, # key 3
    "fran_amount": 67.09   # key 4
}
Run Code Online (Sandbox Code Playgroud)

这是它实际保存的方式:

{
    "eva_fee": 14.37,     # key 2
    "str_fee": 3.17,      # key 1
    "fran_amount": 67.09, # key 4
    "fran_royalty": 14.37 # key 3
}
Run Code Online (Sandbox Code Playgroud)

目的:

在您回答“在接收端消耗 JSON 时排序无关紧要”之前,请先停下来想一想……然后请继续阅读

我需要按照我需要的方式对键进行排序,因为使用此 JSON 的客户端界面正在向开发人员显示 JSON,这些开发人员需要按照文档告诉他们的顺序来显示 JSON。以及它需要的原因按照这个顺序是首先显示什么计算发生的过程:

正确的顺序告诉开发者:

首先str_fee应用 ,然后应用eva_fee,然后是fran_royalty... 制作fran_amount结束金额。

但是根据jsonb排序方式,它错误地告诉我们的开发人员:

首先eva_fee应用 ,然后应用str_fee,然后是fran_amount... 制作fran_royalty结束金额。

小智 15

实际上,它们不是按字母顺序排序,而是按密钥长度然后按字母顺序排序,这解释了您得到的顺序。该jsonb类型已被创建为json用于写入和访问数据的类型的更好版本,并且它们更改键顺序可能是为了索引和搜索目的。如果你希望你的键顺序不改变,你可以使用json在数据库中存储数据时不改变键顺序的类型。

希望能帮助到你。

  • https://www.postgresql.org/docs/10/datatype-json.html:“与 json 相比,jsonb 不保留空格,不保留对象键的顺序,也不保留重复的对象钥匙。” (2认同)

Sla*_*lav 5

Postgres的文档建议使用json类型保护对象键的顺序:

一般来说,大多数应用程序应该更喜欢将 JSON 数据存储为 jsonb,除非有非常特殊的需求,例如关于对象键排序的遗留假设。


skp*_*rin 3

[更新于 2021/02/12]请参阅下面我的“已接受”答案的评论@mu is too short(我不想接受我自己的答案,因为它是 Rails hack)。

基本上为了保存列中的顺序jsonb,我需要使用一个数组(即[{str_fee: 6}, {eva_fee: 11}, ...])。


[老套路答案]

我找不到有关如何修改jsonb保存/更新行为的任何信息,但您可以控制如何as_json从 Rails 模型返回。

因此,不要通过self.amount_splits直接调用列来返回 JSON(它将以错误的键顺序返回)...手动分解每个键

注意:只有当您提前知道键名时,这才有效...如果键名是在您知道之前动态创建的,您将需要尝试其他方法...可能会将 JSON 保存为字符串而不是保存为一个哈希值。

class Transaction < ApplicationRecord
  store_accessor :amount_splits, :str_fee, :eva_fee, :fran_royalty, :fran_amount

  [...]

  def as_json(options={})
    # simple JSON response:
    json = {
      [...]
      "amount_splits"   => {
        "str_fee"       => self.str_fee,
        "eva_fee"       => self.eva_fee,
        "fran_royalty"  => self.fran_royalty,
        "fran_amount"   => self.fran_amount
      },
      [...]
    }
    return json
  end

  [...]

end
Run Code Online (Sandbox Code Playgroud)

注意:我已经显着缩写了我的自定义as_json方法,只保留它将返回的 JSON 的相关部分

  • 数组会更合适。数组本质上在任何地方都是有序的:Ruby、JSON、JavaScript、Go、Python……更像是 `[{str_fee: 6}, {eva_fee: 11}, ...]` 或 `[{type: 'str_fee' , amount: 6}, {type: 'eva_fee', amount: 11}, ...]` 在任何地方都是一样的,如果需要的话,操作订单会更容易。 (3认同)