Skip to content

Commit b0f699e

Browse files
committed
Implement JSON.Encoder protocol
1 parent 5c63604 commit b0f699e

File tree

6 files changed

+67
-11
lines changed

6 files changed

+67
-11
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Dev
44

5+
- Support the `JSON.Encode` protocol in Elixir 1.18+.
6+
57
## v0.7.3 (2024-12-12)
68

79
### Enhancements

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ All data structures offer:
102102
- well-documented APIs that are consistent with the standard library
103103
- implementation of `Inspect`, `Enumerable` and `Collectable` protocols
104104
- implementation of the `Access` behaviour
105+
- implementation of the `JSON.Encoder` protocol (on Elixir 1.18+)
105106
- (optional if `Jason` is installed) implemention of the `Jason.Encoder`
106107
protocol
107108

lib/ord_map.ex

+23-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ defmodule Aja.OrdMap do
1515
`Aja.OrdMap`:
1616
- provides efficient (logarithmic) access: it is not a simple list of tuples
1717
- implements the `Access` behaviour, `Enum` / `Inspect` / `Collectable` protocols
18+
- implements the `JSON.Encoder` protocol (on Elixir 1.18+)
1819
- optionally implements the `Jason.Encoder` protocol if `Jason` is installed
1920
2021
## Examples
@@ -98,7 +99,9 @@ defmodule Aja.OrdMap do
9899
true
99100
100101
101-
## With `Jason`
102+
## JSON encoding
103+
104+
Both `JSON.Encoder` and `Jason.Encoder` are supported.
102105
103106
iex> Aja.OrdMap.new([{"un", 1}, {"deux", 2}, {"trois", 3}]) |> Jason.encode!()
104107
"{\"un\":1,\"deux\":2,\"trois\":3}"
@@ -1655,6 +1658,25 @@ defmodule Aja.OrdMap do
16551658
end
16561659
end
16571660

1661+
if Code.ensure_loaded?(JSON.Encoder) do
1662+
defimpl JSON.Encoder do
1663+
def encode(map, encoder) do
1664+
key_values =
1665+
Aja.Enum.map_intersperse(map, ?,, fn {key, value} ->
1666+
[key(key, encoder), ?:, encoder.(value, encoder)]
1667+
end)
1668+
1669+
[?{, key_values, ?}]
1670+
end
1671+
1672+
# ported from :json
1673+
defp key(key, encode) when is_binary(key), do: encode.(key, encode)
1674+
defp key(key, encode) when is_atom(key), do: encode.(Atom.to_string(key), encode)
1675+
defp key(key, _encode) when is_integer(key), do: [?", Integer.to_string(key), ?"]
1676+
defp key(key, _encode) when is_float(key), do: [?", Float.to_string(key), ?"]
1677+
end
1678+
end
1679+
16581680
if Code.ensure_loaded?(Jason.Encoder) do
16591681
defimpl Jason.Encoder do
16601682
def encode(map, opts) do

lib/vector.ex

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ defmodule Aja.Vector do
1919
- is heavily optimized and should offer higher performance in most use cases, especially "loops" like `map/2` / `to_list/1` / `foldl/3`
2020
- mirrors most of the `Enum` module API (together with `Aja.Enum`) with highly optimized versions for vectors (`Aja.Enum.join/1`, `Aja.Enum.sum/1`, `Aja.Enum.random/1`...)
2121
- supports negative indexing (e.g. `-1` corresponds to the last element)
22+
- implements the `JSON.Encoder` protocol (on Elixir 1.18+)
2223
- optionally implements the `Jason.Encoder` protocol if `Jason` is installed
2324
2425
Note: most of the design is inspired by
@@ -2039,6 +2040,15 @@ defmodule Aja.Vector do
20392040
end
20402041
end
20412042

2043+
if Code.ensure_loaded?(JSON.Encoder) do
2044+
defimpl JSON.Encoder do
2045+
def encode(vector, encoder) do
2046+
values = Aja.Enum.map_intersperse(vector, ?,, fn value -> encoder.(value, encoder) end)
2047+
[?[, values, ?]]
2048+
end
2049+
end
2050+
end
2051+
20422052
if Code.ensure_loaded?(Jason.Encoder) do
20432053
defimpl Jason.Encoder do
20442054
def encode(vector, opts) do

test/ord_map_test.exs

+20-10
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,15 @@ defmodule Aja.OrdMapTest do
8888
Aja.OrdMap.new([{:foo, :atom}, {5, :integer}]) |> inspect()
8989
end
9090

91-
if Version.compare(System.version(), "1.14.0") != :lt do
92-
test "from_struct/1" do
93-
ord_map = %User{name: "John", age: 44} |> Aja.OrdMap.from_struct()
94-
expected = Aja.OrdMap.new(name: "John", age: 44)
95-
assert ^expected = ord_map
96-
97-
ord_map = Aja.OrdMap.from_struct(User)
98-
expected = Aja.OrdMap.new(name: nil, age: nil)
99-
assert ^expected = ord_map
100-
end
91+
@tag skip: Version.compare(System.version(), "1.14.0") == :lt
92+
test "from_struct/1" do
93+
ord_map = %User{name: "John", age: 44} |> Aja.OrdMap.from_struct()
94+
expected = Aja.OrdMap.new(name: "John", age: 44)
95+
assert ^expected = ord_map
96+
97+
ord_map = Aja.OrdMap.from_struct(User)
98+
expected = Aja.OrdMap.new(name: nil, age: nil)
99+
assert ^expected = ord_map
101100
end
102101

103102
test "get_and_update/3" do
@@ -117,5 +116,16 @@ defmodule Aja.OrdMapTest do
117116
Aja.OrdMap.get_and_update!(ord_map, :b, fn value -> value end)
118117
end
119118
end
119+
120+
@tag skip: Version.compare(System.version(), "1.18.0-rc.0") == :lt
121+
test "JSON.encode!/1" do
122+
result = Aja.OrdMap.new([{"un", 1}, {"deux", 2}, {"trois", 3}]) |> JSON.encode!()
123+
assert result == "{\"un\":1,\"deux\":2,\"trois\":3}"
124+
end
125+
126+
test "Jason.encode!/1" do
127+
result = Aja.OrdMap.new([{"un", 1}, {"deux", 2}, {"trois", 3}]) |> Jason.encode!()
128+
assert result == "{\"un\":1,\"deux\":2,\"trois\":3}"
129+
end
120130
end
121131
end

test/vector_test.exs

+11
Original file line numberDiff line numberDiff line change
@@ -542,5 +542,16 @@ defmodule Aja.VectorTest do
542542
assert [18, 19] == Aja.Vector.new(1..20) |> Enum.slice(-3, 2)
543543
assert Enum.to_list(2..99) == Aja.Vector.new(1..100) |> Enum.slice(1..98)
544544
end
545+
546+
@tag skip: Version.compare(System.version(), "1.18.0-rc.0") == :lt
547+
test "JSON.encode!/1" do
548+
result = Aja.Vector.new(["un", 2, :trois]) |> JSON.encode!()
549+
assert result == "[\"un\",2,\"trois\"]"
550+
end
551+
552+
test "Jason.encode!/1" do
553+
result = Aja.Vector.new(["un", 2, :trois]) |> Jason.encode!()
554+
assert result == "[\"un\",2,\"trois\"]"
555+
end
545556
end
546557
end

0 commit comments

Comments
 (0)