Fl4m3Ph03n1x

Fl4m3Ph03n1x

How to have custom enconding for struct using Jason?

Background

I am trying to encode a structure into json format using the Jason library. However, this is not working as expected.

Code

Let’s assume I have this struct:

defmodule Test do
   defstruct [:foo, :bar, :baz]
end

And that when using Jason.enconde(%Test{foo: 1, bar: 2, baz:3 }) I want this json to be created:

%{"foo" => 1, "banana" => 5}

Error

It is my understanding that to achieve this I need to implement the Jason.Enconder protocol in my struct:
https://hexdocs.pm/jason/Jason.Encoder.html

defmodule Test do
   defstruct [:foo, :bar, :baz]
   
   defimpl Jason.Encoder do
      @impl Jason.Encoder 
      def encode(value, opts) do
         Jason.Encode.map(%{foo: Map.get(value, :foo), banana: Map.get(value, :bar, 0) + Map.get(value, :baz, 0)}, opts)
      end
   end
end

However, this will not work:

Jason.encode(%Test{foo: 1, bar: 2, baz: 3})
{:error,
 %Protocol.UndefinedError{
   description: "Jason.Encoder protocol must always be explicitly implemented.\n\nIf you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:\n\n    @derive {Jason.Encoder, only: [....]}\n    defstruct ...\n\nIt is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:\n\n    @derive Jason.Encoder\n    defstruct ...\n\nFinally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:\n\n    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])\n    Protocol.derive(Jason.Encoder, NameOfTheStruct)\n",
   protocol: Jason.Encoder,
   value: %Test{bar: 2, baz: 3, foo: 1}
 }}

From what I understand, it looks like I can only select/exclude keys to serialize, I cannot transform/add new keys.
Since I own the structure in question, using Protocol.derive is not necessary.

However I fail to understand how I can leverage the Jason.Encoder protocol to achieve what I want.

Questions

  1. Is my objective possible using the Jason library, or is this a limitation?
  2. Am I miss understanding the documentation and doing something incorrect?

Marked As Solved

Fl4m3Ph03n1x

Fl4m3Ph03n1x

Answer

Turns out there is nothing wrong with the code itself.

The issue here is that I was trying these examples in the iex shell. Due to Protocol Consolidation this means that the protocol will be consolidated at compile time. Therefore, because when I launch iex there is nothing regarding the Jason.Encoder protocol compiled, I loose the chance to do it runtime (and iex is runtime).

This also affects tests in an interesting way, to fix them you can either disable Protocol Consolidation wen running under Mix.env() == :test (but it will absolutely hammer your test’s performance) or you can do it at compile time with some extra options.

A very good answer was also given by the community:

And I recommend you read the full thread if you want all the juicy details that I have quickly summarized here.

Popular Backend topics Top

New
Fl4m3Ph03n1x
Background I am trying out polymorphic typing with dialyzer. As an example I am using the famous Option type (aka, Maybe Monad) that is n...
New
JimmyCarterSon
Hello, I am working on a new application with Elixir, Dish_out. I want to see Data I follow this tutorial with Elixir Casts. However, I ...
New
MarkIden
Hi, Recommend pls your favorite learning resources in Go, with best books, podcasts etc.
/go
New
Sumityadav
Hello all, I am new to learning Java Programming and want to learn java from scratch. I was writing a Java program to get the first and l...
New
harwind
I’m presently working on a backend development project to build a RESTful API using Python and Flask. The Scaler backend developer site h...
New
pillaiindu
What is the difference between using :references and :belongs_to in the following command? bin/rails generate scaffold LineItem product:...
New
jaeyson
Hi! I have clarifications (please correct me, as I mostly mix/confuse this details) with the following: The term RAG here where it read...
New
AstonJ
If you’re getting errors like this: psql: error: connection to server on socket “/tmp/.s.PGSQL.5432” failed: No such file or directory ...
New
apoorv-2204
Anyone know how to get in golang? I am from elixir background?.
New

Other popular topics Top

New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
AstonJ
Thanks to @foxtrottwist’s and @Tomas’s posts in this thread: Poll: Which code editor do you use? I bought Onivim! :nerd_face: https://on...
New
Exadra37
I am a Linux user since 2012, more or less, and I always use Ubuntu on my computers, and my last 2 laptops have been used Thinkpads, wher...
New
AstonJ
I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl: The Ser...
New
mafinar
Crystal recently reached version 1. I had been following it for awhile but never got to really learn it. Most languages I picked up out o...
New
AstonJ
Saw this on TikTok of all places! :lol: Anyone heard of them before? Lite:
New
Maartz
Hi folks, I don’t know if I saw this here but, here’s a new programming language, called Roc Reminds me a bit of Elm and thus Haskell. ...
New
First poster: bot
The overengineered Solution to my Pigeon Problem. TL;DR: I built a wifi-equipped water gun to shoot the pigeons on my balcony, controlle...
New
New