diff --git a/config/config.exs b/config/config.exs index 9656d8e51a..77a4e046c6 100644 --- a/config/config.exs +++ b/config/config.exs @@ -5,7 +5,8 @@ config :logger, :console, format: "\n$time $metadata[$level] $message\n" config :phoenix, - json_library: Jason, + # TODO: Remove the `json_library` check once `JSON` becomes the standard `Phoenix.json_library/1` + json_library: (if Code.ensure_loaded?(JSON), do: JSON, else: Jason), stacktrace_depth: 20, trim_on_html_eex_engine: false diff --git a/guides/controllers.md b/guides/controllers.md index 572f4407af..40ae3abdbe 100644 --- a/guides/controllers.md +++ b/guides/controllers.md @@ -94,7 +94,7 @@ end Now [`/hello/Frank`] in your browser should display `From messenger Frank` as plain text without any HTML. -A step beyond this is rendering pure JSON with the [`json/2`] function. We need to pass it something that the [Jason library](`Jason`) can decode into JSON, such as a map. (Jason is one of Phoenix's dependencies.) +A step beyond this is rendering pure JSON with the [`json/2`] function. We need to pass it something that the [JSON library](https://hexdocs.pm/elixir/JSON.html) can decode into JSON, such as a map. ```elixir def show(conn, %{"messenger" => messenger}) do diff --git a/guides/json_and_apis.md b/guides/json_and_apis.md index e2705c9a3e..7e1683fc6c 100644 --- a/guides/json_and_apis.md +++ b/guides/json_and_apis.md @@ -171,7 +171,7 @@ defmodule HelloWeb.UrlJSON do end ``` -This view is very simple. The `index` function receives all URLs, and converts them into a list of maps. Those maps are placed inside the data key at the root, exactly as we saw when interfacing with our application from `cURL`. In other words, our JSON view converts our complex data into simple Elixir data-structures. Once our view layer returns, Phoenix uses the `Jason` library to encode JSON and send the response to the client. +This view is very simple. The `index` function receives all URLs, and converts them into a list of maps. Those maps are placed inside the data key at the root, exactly as we saw when interfacing with our application from `cURL`. In other words, our JSON view converts our complex data into simple Elixir data-structures. Once our view layer returns, Phoenix uses the built-in [JSON library](https://hexdocs.pm/elixir/1.18/JSON.html) to encode JSON and send the response to the client. If you explore the remaining controller, you will learn the `show` action is similar to the `index` one. For `create`, `update`, and `delete` actions, Phoenix uses one other important feature, called "Action fallback". diff --git a/guides/plug.md b/guides/plug.md index 308a60b877..36c2766f2e 100644 --- a/guides/plug.md +++ b/guides/plug.md @@ -153,7 +153,7 @@ The default endpoint plugs do quite a lot of work. Here they are in order: - `Plug.Telemetry` - adds instrumentation points so Phoenix can log the request path, status code and request time by default. -- `Plug.Parsers` - parses the request body when a known parser is available. By default, this plug can handle URL-encoded, multipart and JSON content (with `Jason`). The request body is left untouched if the request content-type cannot be parsed. +- `Plug.Parsers` - parses the request body when a known parser is available. By default, this plug can handle URL-encoded, multipart and JSON content. The request body is left untouched if the request content-type cannot be parsed. - `Plug.MethodOverride` - converts the request method to PUT, PATCH or DELETE for POST requests with a valid `_method` parameter. diff --git a/installer/templates/phx_single/config/config.exs b/installer/templates/phx_single/config/config.exs index 11496cdbc4..a15fcba6f3 100644 --- a/installer/templates/phx_single/config/config.exs +++ b/installer/templates/phx_single/config/config.exs @@ -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 # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/installer/templates/phx_umbrella/config/extra_config.exs b/installer/templates/phx_umbrella/config/extra_config.exs index 713d9b6bc4..9af3ece5fa 100644 --- a/installer/templates/phx_umbrella/config/extra_config.exs +++ b/installer/templates/phx_umbrella/config/extra_config.exs @@ -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. diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs index c1c1166f62..f97068a17c 100644 --- a/installer/test/phx_new_test.exs +++ b/installer/test/phx_new_test.exs @@ -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" diff --git a/installer/test/phx_new_umbrella_test.exs b/installer/test/phx_new_umbrella_test.exs index 61258f8cbe..aaa8cbb70d 100644 --- a/installer/test/phx_new_umbrella_test.exs +++ b/installer/test/phx_new_umbrella_test.exs @@ -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" diff --git a/integration_test/config/config.exs b/integration_test/config/config.exs index a596f1c7d3..308398d6cc 100644 --- a/integration_test/config/config.exs +++ b/integration_test/config/config.exs @@ -1,6 +1,7 @@ import Config -config :phoenix, :json_library, Jason +# TODO: Remove the `json_library` check once `JSON` becomes the standard `Phoenix.json_library/1` +config :phoenix, :json_library, (if Code.ensure_loaded?(JSON), do: JSON, else: Jason) config :swoosh, api_client: false diff --git a/lib/phoenix.ex b/lib/phoenix.ex index 9a4d5b7ac3..0d484dc898 100644 --- a/lib/phoenix.ex +++ b/lib/phoenix.ex @@ -45,6 +45,7 @@ defmodule Phoenix do """ def json_library do + # TODO: Change the default json library to JSON in Phoenix v2.0 Application.get_env(:phoenix, :json_library, Jason) end diff --git a/test/phoenix/socket/v1_json_serializer_test.exs b/test/phoenix/socket/v1_json_serializer_test.exs index 62652d3f26..0bfb7f4881 100644 --- a/test/phoenix/socket/v1_json_serializer_test.exs +++ b/test/phoenix/socket/v1_json_serializer_test.exs @@ -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 Phoenix.json_library().decode!(encoded) == %{ "event" => "e", "payload" => "m", "ref" => nil, @@ -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 Phoenix.json_library().decode!(encoded) == %{ "event" => "phx_reply", "payload" => %{"response" => nil, "status" => nil}, "ref" => "null", @@ -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 Phoenix.json_library().decode!(encoded) == %{ "event" => "e", "payload" => "m", "ref" => nil, diff --git a/test/phoenix/socket/v2_json_serializer_test.exs b/test/phoenix/socket/v2_json_serializer_test.exs index c534ff47d3..acfb2ef7e9 100644 --- a/test/phoenix/socket/v2_json_serializer_test.exs +++ b/test/phoenix/socket/v2_json_serializer_test.exs @@ -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 Phoenix.json_library().decode!(encoded) == [ nil, nil, "t", diff --git a/test/phoenix/test/conn_test.exs b/test/phoenix/test/conn_test.exs index 724de7a040..ffadd98065 100644 --- a/test/phoenix/test/conn_test.exs +++ b/test/phoenix/test/conn_test.exs @@ -328,13 +328,19 @@ 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 -> + json_error = + case Phoenix.json_library() do + JSON -> JSON.DecodeError + Jason -> Jason.DecodeError + end + + assert_raise json_error, + ~r/invalid byte 111 at position \(byte offset\) 0|unexpected byte at position 0: 0x6F \("o"\)/, 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_error, ~r/unexpected end of JSON binary at position \(byte offset\) 0|unexpected end of input at position 0/, fn -> build_conn(:get, "/") |> put_resp_content_type("application/json") |> resp(200, "")