From 4eaf30e66e6b55a12cf2f3ddadc869049c34b4f9 Mon Sep 17 00:00:00 2001 From: Gilbert Bishop-White Date: Fri, 8 Nov 2024 10:27:43 +0000 Subject: [PATCH 1/2] Allow deriving Jason.Encoder with sorted keys --- lib/encoder.ex | 21 ++++++++++++++++++++- test/encode_test.exs | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/encoder.ex b/lib/encoder.ex index 95fbf0c..f7c235b 100644 --- a/lib/encoder.ex +++ b/lib/encoder.ex @@ -127,6 +127,17 @@ defimpl Jason.Encoder, for: Any do Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) Protocol.derive(Jason.Encoder, NameOfTheStruct) + + You can also sort the keys in the output JSON, either according to \ + Erlang's term ordering: + + @derive {Jason.Encoder, sort_keys: true} + defstruct ... + + Or using a custom sort function, as passed to `Enum.sort/2`: + + @derive {Jason.Encoder, sort_keys: &(&1>=&2)} + defstruct ... """ end @@ -138,7 +149,15 @@ defimpl Jason.Encoder, for: Any do end defp fields_to_encode(struct, opts) do - fields = Map.keys(struct) + fields = case Keyword.get(opts, :sort_keys) do + true -> + Map.keys(struct) |> Enum.sort() + sort_fn when is_function(sort_fn, 2) -> + Map.keys(struct) + |> Enum.sort(sort_fn) + nil -> + Map.keys(struct) + end cond do only = Keyword.get(opts, :only) -> diff --git a/test/encode_test.exs b/test/encode_test.exs index 379d160..27958e1 100644 --- a/test/encode_test.exs +++ b/test/encode_test.exs @@ -135,6 +135,18 @@ defmodule Jason.EncoderTest do defstruct name: "", size: 0 end + defmodule DerivedUsingSortKeys do + @derive {Encoder, sort_keys: true} + defstruct a: 1, b: "", c: nil, d: 4 + end + + + defmodule DerivedUsingCustomSortKeys do + @derive {Encoder, sort_keys: &(&1>=&2)} + defstruct a: 1, b: "", c: nil, d: 4 + end + + defmodule DerivedWeirdKey do @derive Encoder defstruct [:_] @@ -160,6 +172,12 @@ defmodule Jason.EncoderTest do derived_using_except = %DerivedUsingExcept{name: "derived using :except", size: 10} assert to_json(derived_using_except) == ~s({"size":10}) + derived_using_sort_keys = %DerivedUsingSortKeys{} + assert to_json(derived_using_sort_keys) == ~s({"a":1,"b":"","c":null,"d":4}) + + derived_using_custom_sort_keys = %DerivedUsingCustomSortKeys{} + assert to_json(derived_using_custom_sort_keys) == ~s({"d":4,"c":null,"b":"","a":1}) + derived_weird_key = %DerivedWeirdKey{} assert to_json(derived_weird_key) == ~s({"_":null}) end From eb5fb35573db1b7a8eaa51789b59f7235527e38c Mon Sep 17 00:00:00 2001 From: Gilbert Bishop-White Date: Fri, 8 Nov 2024 10:53:25 +0000 Subject: [PATCH 2/2] Move documentation to correct place --- lib/encoder.ex | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/encoder.ex b/lib/encoder.ex index f7c235b..be52574 100644 --- a/lib/encoder.ex +++ b/lib/encoder.ex @@ -10,6 +10,7 @@ defprotocol Jason.Encoder do * `:only` - encodes only values of specified keys. * `:except` - encodes all struct fields except specified keys. + * `:sort_keys` - sorts the keys, optionally with a custom function. By default all keys except the `:__struct__` key are encoded. @@ -51,6 +52,13 @@ defprotocol Jason.Encoder do The actually generated implementations are more efficient computing some data during compilation similar to the macros from the `Jason.Helpers` module. + ## Sorting keys + + You can also sort the keys in the output JSON, either according to Erlang's + term ordering with `@derive {Jason.Encoder, sort_keys: true}` or using a custom + sort function (as passed to `Enum.sort/2`) e.g. + `@derive {Jason.Encoder, sort_keys: &(&1>=&2)}` + ## Explicit implementation If you wish to implement the protocol fully yourself, it is advised to @@ -127,17 +135,6 @@ defimpl Jason.Encoder, for: Any do Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) Protocol.derive(Jason.Encoder, NameOfTheStruct) - - You can also sort the keys in the output JSON, either according to \ - Erlang's term ordering: - - @derive {Jason.Encoder, sort_keys: true} - defstruct ... - - Or using a custom sort function, as passed to `Enum.sort/2`: - - @derive {Jason.Encoder, sort_keys: &(&1>=&2)} - defstruct ... """ end