Skip to content

Commit

Permalink
Encode any JSON key to string, closes #14305
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Mar 3, 2025
1 parent 71da1a2 commit ff628d0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 17 deletions.
44 changes: 31 additions & 13 deletions lib/elixir/lib/json.ex
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,28 @@ end

defimpl JSON.Encoder, for: Map do
def encode(value, encoder) do
:elixir_json.encode_map(value, encoder)
case :maps.next(:maps.iterator(value)) do
:none ->
"{}"

{key, value, iterator} ->
[?{, key(key, encoder), ?:, encoder.(value, encoder) | next(iterator, encoder)]
end
end

defp next(iterator, encoder) do
case :maps.next(iterator) do
:none ->
"}"

{key, value, iterator} ->
[?,, key(key, encoder), ?:, encoder.(value, encoder) | next(iterator, encoder)]
end
end

defp key(key, encoder) when is_atom(key), do: encoder.(Atom.to_string(key), encoder)
defp key(key, encoder) when is_binary(key), do: encoder.(key, encoder)
defp key(key, encoder), do: encoder.(String.Chars.to_string(key), encoder)
end

defimpl JSON.Encoder, for: Duration do
Expand Down Expand Up @@ -262,17 +282,15 @@ defmodule JSON do
Elixir primitive types are encoded to JSON as follows:
| **Elixir** | **JSON** |
|------------------------|----------|
| `integer() \| float()` | Number |
| `true \| false ` | Boolean |
| `nil` | Null |
| `binary()` | String |
| `atom()` | String |
| `list()` | Array |
| `%{binary() => _}` | Object |
| `%{atom() => _}` | Object |
| `%{integer() => _}` | Object |
| **Elixir** | **JSON** |
|----------------------------|----------|
| `integer() \| float()` | Number |
| `true \| false ` | Boolean |
| `nil` | Null |
| `binary()` | String |
| `atom()` | String |
| `list()` | Array |
| `%{String.Chars.t() => _}` | Object |
You may also implement the `JSON.Encoder` protocol for custom data structures.
Some built-in data-structures already derive the `JSON.Encoder` protocol:
Expand Down Expand Up @@ -499,7 +517,7 @@ defmodule JSON do
do: :elixir_json.encode_list(value, encoder)

def protocol_encode(%{} = value, encoder) when not is_map_key(value, :__struct__),
do: :elixir_json.encode_map(value, encoder)
do: JSON.Encoder.Map.encode(value, encoder)

def protocol_encode(value, encoder),
do: JSON.Encoder.encode(value, encoder)
Expand Down
8 changes: 4 additions & 4 deletions lib/elixir/test/elixir/json_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ defmodule JSONTest do
end

test "maps" do
assert JSON.encode!(%{1 => 2, 3.0 => 4.0, key: :bar}) ==
"{\"1\":2,\"3.0\":4.0,\"key\":\"bar\"}"
assert JSON.encode!(%{1 => 2, 3.0 => 4.0, ~c"list" => ~c"list", key: :bar}) ==
"{\"1\":2,\"3.0\":4.0,\"key\":\"bar\",\"list\":[108,105,115,116]}"
end

test "lists" do
Expand Down Expand Up @@ -83,8 +83,8 @@ defmodule JSONTest do
end

test "maps" do
assert protocol_encode(%{1 => 2, 3.0 => 4.0, key: :bar}) ==
"{\"1\":2,\"3.0\":4.0,\"key\":\"bar\"}"
assert protocol_encode(%{1 => 2, 3.0 => 4.0, ~c"list" => ~c"list", key: :bar}) ==
"{\"1\":2,\"3.0\":4.0,\"key\":\"bar\",\"list\":[108,105,115,116]}"
end

test "lists" do
Expand Down

0 comments on commit ff628d0

Please sign in to comment.