Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ config :logger, :console,
format: "\n$time $metadata[$level] $message\n"

config :phoenix,
json_library: Jason,
Comment thread
SteffenDE marked this conversation as resolved.
json_library: JSON,
stacktrace_depth: 20,
trim_on_html_eex_engine: false

Expand Down
4 changes: 2 additions & 2 deletions installer/templates/phx_single/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ config :logger, :default_formatter,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Use built-in `JSON` for JSON parsing in Phoenix
config :phoenix, :json_library, JSON

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we could do a similar runtime detection as in https://github.com/phoenixframework/phoenix/pull/6481/changes#r2356177940, and only configure JSON as the default for Elixir versions where its available.

Same for the umbrella app config template.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a nice idea, yet I don't think it would fly.

The first option is to check Elixir version during phx.new, but the Elixir version that generates the project is not guaranteed to match the Elixir version that runs the project. So it sounds like a breaking change.

The second option is to embed this check into new projects. Technically it should work, but I'm not sure that such checks are awkward and probably are not something you would like to see in new projects.


# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
Expand Down
4 changes: 2 additions & 2 deletions installer/templates/phx_umbrella/config/extra_config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ config :logger, :default_formatter,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Use built-in `JSON` for JSON parsing in Phoenix
config :phoenix, :json_library, JSON

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
Expand Down
2 changes: 1 addition & 1 deletion installer/test/phx_new_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ defmodule Mix.Tasks.Phx.NewTest do
assert_file("phx_blog/config/config.exs", fn file ->
assert file =~ "ecto_repos: [PhxBlog.Repo]"
assert file =~ "generators: [timestamp_type: :utc_datetime]"
assert file =~ "config :phoenix, :json_library, Jason"
assert file =~ "config :phoenix, :json_library, JSON"
assert file =~ ~s[cd: Path.expand("../assets", __DIR__),]
refute file =~ "namespace: PhxBlog"
refute file =~ "config :phx_blog, :generators"
Expand Down
2 changes: 1 addition & 1 deletion installer/test/phx_new_umbrella_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
assert file =~ ~r/config :esbuild/
assert file =~ "cd: Path.expand(\"../apps/phx_umb_web/assets\", __DIR__)"
assert file =~ ~S[import_config "#{config_env()}.exs"]
assert file =~ "config :phoenix, :json_library, Jason"
assert file =~ "config :phoenix, :json_library, JSON"
assert file =~ "ecto_repos: [PhxUmb.Repo]"
assert file =~ ":phx_umb_web, PhxUmbWeb.Endpoint"
assert file =~ "generators: [context_app: :phx_umb]\n"
Expand Down
2 changes: 1 addition & 1 deletion integration_test/config/config.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config

config :phoenix, :json_library, Jason
config :phoenix, :json_library, JSON

config :swoosh, api_client: false

Expand Down
4 changes: 3 additions & 1 deletion lib/phoenix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule Phoenix do
"""
use Application

@default_json_library if Code.ensure_loaded?(JSON), do: JSON, else: Jason

@doc false
def start(_type, _args) do
# Warm up caches
Expand Down Expand Up @@ -45,7 +47,7 @@ defmodule Phoenix do

"""
def json_library do
Application.get_env(:phoenix, :json_library, Jason)
Application.get_env(:phoenix, :json_library, @default_json_library)
Comment thread
SteffenDE marked this conversation as resolved.
Outdated

@saleyn saleyn Jun 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider :persistent_term, which is roughly 30-100% faster:

defmodule JSON.Adapter do
  @default_json_library if Code.ensure_loaded?(JSON), do: JSON, else: Jason

  def json_library do
    case :persistent_term.get(:json_library, nil) do
      nil ->
        module = :application.get_env(:phoenix, :json_library, @default_json_library)
        :persistent_term.put(:json_library, module)
        module
      module ->
        module
    end
  end
end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL persistent_term and it looks cool, but I'm not sure if we are in the position of adding anything significant to this PR. I'm happy to add it, but it's @SteffenDE call at this point

end

@doc """
Expand Down
6 changes: 3 additions & 3 deletions test/phoenix/socket/v1_json_serializer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Phoenix.Socket.V1.JSONSerializerTest do
msg = %Message{topic: "t", event: "e", payload: "m"}
encoded = encode!(@serializer, msg)

assert Jason.decode!(encoded) == %{
assert JSON.decode!(encoded) == %{
"event" => "e",
"payload" => "m",
"ref" => nil,
Expand All @@ -36,7 +36,7 @@ defmodule Phoenix.Socket.V1.JSONSerializerTest do
msg = %Reply{topic: "t", ref: "null"}
encoded = encode!(@serializer, msg)

assert Jason.decode!(encoded) == %{
assert JSON.decode!(encoded) == %{
"event" => "phx_reply",
"payload" => %{"response" => nil, "status" => nil},
"ref" => "null",
Expand All @@ -61,7 +61,7 @@ defmodule Phoenix.Socket.V1.JSONSerializerTest do
msg = %Broadcast{topic: "t", event: "e", payload: "m"}
encoded = fastlane!(@serializer, msg)

assert Jason.decode!(encoded) == %{
assert JSON.decode!(encoded) == %{
"event" => "e",
"payload" => "m",
"ref" => nil,
Expand Down
2 changes: 1 addition & 1 deletion test/phoenix/socket/v2_json_serializer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ defmodule Phoenix.Socket.V2.JSONSerializerTest do
msg = %Reply{topic: "t", payload: %{m: 1}}
encoded = encode!(@serializer, msg)

assert Jason.decode!(encoded) == [
assert JSON.decode!(encoded) == [
nil,
nil,
"t",
Expand Down
6 changes: 3 additions & 3 deletions test/phoenix/test/conn_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -328,13 +328,13 @@ defmodule Phoenix.Test.ConnTest do
build_conn(:get, "/") |> resp(200, "ok") |> json_response(200)
end

assert_raise Jason.DecodeError,
"unexpected byte at position 0: 0x6F (\"o\")", fn ->
assert_raise JSON.DecodeError,
"invalid byte 111 at position (byte offset) 0", fn ->
build_conn(:get, "/") |> put_resp_content_type("application/json")
|> resp(200, "ok") |> json_response(200)
end

assert_raise Jason.DecodeError, ~r/unexpected end of input at position 0/, fn ->
assert_raise JSON.DecodeError, ~r/unexpected end of JSON binary at position \(byte offset\) 0/, fn ->
build_conn(:get, "/")
|> put_resp_content_type("application/json")
|> resp(200, "")
Expand Down