Open zachallaun opened 2 months ago
Thanks for this! I think protocols is the deal breaker for now. This is possible today
Req.post!(url, json: %{now: Time.utc_now()})
and there's no replacement until Elixir gets JSON with protocols. At that point, it would be a breaking change too, instead of using Jason.Encoder we'd use JSON.Encoder, but I'm OK with that and I'll document it on Req v1.0. Even if Jason becomes a tiny shim over Elixir's JSON, I'd rather not depend on it anyway.
regarding :decode_json
option today, I can deprecate it in favour of passing, say, decode_json: &:json.decode(&1, ...)
, so that's not a big deal.
Or, decoders: [json: &:json.decode(&1, ...)]
. So yeah, we're good, we have long-term backwards compatible options.
Okay, so to clarify the "plan" a bit:
:decode_json
opts in favor of a :decode_json
function (and presumably add an :encode_json
as well?), which could allow using :json
if desiredDoes that sound about right?
With the release of
:json
in OTP 27, I think it's worth discussing how Req might support its (optional) use. I don't feel strongly that Req should support it (more on that below), but I figured a discussion would be worth having that could at least be pointed to in the future when the topic comes up!Some points for context:
:json
:Jason.encode_to_iodata!/1
(opts not supported) - corresponds to:json.encode/1
(returns iodata and raises on error)Jason.encode!/1
(opts not supported) - equivalent tovalue |> :json.encode() |> IO.iodata_to_binary()
Jason.decode/2
(opts supported with:decode_json
) - no direct correspondence,:json.decode/3
raises on error and does not support high-level opts likekeys: :atoms
, but requires that the same effect be implemented using callbacks:json
is the protocol support that Jason provides for struct encoding. Whereas Jason will error when attempting to encode a struct that doesn't implementJason.Encoder
,:json
will happily encode to{"__struct__": "Elixir.MyStruct", ...}
.Given that
:json
is not a drop-in replacement for Jason, I see a few ways forward::json
. We could wait for that and continue to depend onJason
. (It's possible that this wrapper, if accepted, would not be implemented until Elixir drops support for OTP 26, which I believe is 1.19 at the earliest.):json
on the user: make Jason an optional dependency and add JSON encoder/decoder options that default to usingJason
if present or raise an error suggesting to add the dependency otherwise.:decode_json
options could be passed to the decoder function; it would be up to the user to implement them in terms of:json
if they've overridden the default Jason decoder.:json
on Req: make Jason an optional dependency and provide encoder/decoder implementations in order ofJason > :json > raise an error
.:decode_json
options. I don't think it's reasonable for a Req-provided:json
implementation to accept every option thatJason
does.Jason
and:json
when it comes to struct encoding.This ended up a bit longer than I anticipated, but hopefully is a decent jumping-off point.