Skip to content

Commit 75677b9

Browse files
committed
Encode any JSON key to string, closes #14305 (#14309)
1 parent 25ab648 commit 75677b9

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
@@ -146,8 +146,30 @@ end
146146

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

153175
defimpl JSON.Encoder, for: [Date, Time, NaiveDateTime, DateTime, Duration] do
@@ -175,17 +197,15 @@ defmodule JSON do
175197
176198
Elixir built-in data structures are encoded to JSON as follows:
177199
178-
| **Elixir** | **JSON** |
179-
|------------------------|----------|
180-
| `integer() \| float()` | Number |
181-
| `true \| false ` | Boolean |
182-
| `nil` | Null |
183-
| `binary()` | String |
184-
| `atom()` | String |
185-
| `list()` | Array |
186-
| `%{binary() => _}` | Object |
187-
| `%{atom() => _}` | Object |
188-
| `%{integer() => _}` | Object |
200+
| **Elixir** | **JSON** |
201+
|----------------------------|----------|
202+
| `integer() \| float()` | Number |
203+
| `true \| false ` | Boolean |
204+
| `nil` | Null |
205+
| `binary()` | String |
206+
| `atom()` | String |
207+
| `list()` | Array |
208+
| `%{String.Chars.t() => _}` | Object |
189209
190210
You may also implement the `JSON.Encoder` protocol for custom data structures.
191211
@@ -403,7 +423,7 @@ defmodule JSON do
403423
do: :elixir_json.encode_list(value, encoder)
404424

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

408428
def protocol_encode(value, encoder),
409429
do: JSON.Encoder.encode(value, encoder)

lib/elixir/test/elixir/json_test.exs

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ defmodule JSONTest do
3333
end
3434

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

4040
test "lists" do
@@ -80,8 +80,8 @@ defmodule JSONTest do
8080
end
8181

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

8787
test "lists" do

0 commit comments

Comments
 (0)