Skip to content

Commit 65ae623

Browse files
authored
Encode any JSON key to string, closes #14305 (#14309)
1 parent 88605cf commit 65ae623

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

lib/elixir/lib/json.ex

+33-13
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,30 @@ end
149149

150150
defimpl JSON.Encoder, for: Map do
151151
def encode(value, encoder) do
152-
:elixir_json.encode_map(value, encoder)
152+
case :maps.next(:maps.iterator(value)) do
153+
:none ->
154+
"{}"
155+
156+
{key, value, iterator} ->
157+
[?{, key(key, encoder), ?:, encoder.(value, encoder) | next(iterator, encoder)]
158+
end
153159
end
160+
161+
defp next(iterator, encoder) do
162+
case :maps.next(iterator) do
163+
:none ->
164+
"}"
165+
166+
{key, value, iterator} ->
167+
[?,, key(key, encoder), ?:, encoder.(value, encoder) | next(iterator, encoder)]
168+
end
169+
end
170+
171+
# Erlang supports only numbers, binaries, and atoms as keys,
172+
# we support anything that implements the String.Chars protocol.
173+
defp key(key, encoder) when is_atom(key), do: encoder.(Atom.to_string(key), encoder)
174+
defp key(key, encoder) when is_binary(key), do: encoder.(key, encoder)
175+
defp key(key, encoder), do: encoder.(String.Chars.to_string(key), encoder)
154176
end
155177

156178
defimpl JSON.Encoder, for: Duration do
@@ -262,17 +284,15 @@ defmodule JSON do
262284
263285
Elixir primitive types are encoded to JSON as follows:
264286
265-
| **Elixir** | **JSON** |
266-
|------------------------|----------|
267-
| `integer() \| float()` | Number |
268-
| `true \| false ` | Boolean |
269-
| `nil` | Null |
270-
| `binary()` | String |
271-
| `atom()` | String |
272-
| `list()` | Array |
273-
| `%{binary() => _}` | Object |
274-
| `%{atom() => _}` | Object |
275-
| `%{integer() => _}` | Object |
287+
| **Elixir** | **JSON** |
288+
|----------------------------|----------|
289+
| `integer() \| float()` | Number |
290+
| `true \| false ` | Boolean |
291+
| `nil` | Null |
292+
| `binary()` | String |
293+
| `atom()` | String |
294+
| `list()` | Array |
295+
| `%{String.Chars.t() => _}` | Object |
276296
277297
You may also implement the `JSON.Encoder` protocol for custom data structures.
278298
Some built-in data-structures already derive the `JSON.Encoder` protocol:
@@ -499,7 +519,7 @@ defmodule JSON do
499519
do: :elixir_json.encode_list(value, encoder)
500520

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

504524
def protocol_encode(value, encoder),
505525
do: JSON.Encoder.encode(value, encoder)

lib/elixir/test/elixir/json_test.exs

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ defmodule JSONTest do
3636
end
3737

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

4343
test "lists" do
@@ -83,8 +83,8 @@ defmodule JSONTest do
8383
end
8484

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

9090
test "lists" do

0 commit comments

Comments
 (0)