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.

Where Next?

Popular Backend topics Top

Fl4m3Ph03n1x
Background I am trying to get a Github Action working with Windows and Bakeware because I am trying to create a release using it. Howeve...
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. very new to Elixir lang I have only been doing it for about 2 weeks. I recently started following this tutorial todo list, ...
New
Fl4m3Ph03n1x
Background So, I am playing around with a concept named “NewType” and I am taking inspiration from languages like F# and Scala. My objec...
New
JimmyCarterSon
I am confused about the Schema setup, I am setting up a new application and I want to seed files in it as well. I tried to mix to create...
New
MarkIden
Hi, Recommend pls your favorite learning resources in Go, with best books, podcasts etc.
/go
New
harwind
I have a large SQL database with millions of records, and I’ve identified duplicate entries. What’s the most efficient way to find and re...
New
Fl4m3Ph03n1x
Background I have an umbrella app where I use a dependecy called ETS. This dependency has a type called set_options that I use in some of...
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
Patricia-Mendes13
Hi guys!! I´m studying and got a Full stack course but the course lacked a lot of support and and info to learn as it´s a course after wo...
New

Other popular topics Top

Devtalk
Hello Devtalk World! Please let us know a little about who you are and where you’re from :nerd_face:
New
siddhant3030
I’m thinking of buying a monitor that I can rotate to use as a vertical monitor? Also, I want to know if someone is using it for program...
New
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
AstonJ
Continuing the discussion from Thinking about learning Crystal, let’s discuss - I was wondering which languages don’t GC - maybe we can c...
New
Margaret
Hello everyone! This thread is to tell you about what authors from The Pragmatic Bookshelf are writing on Medium.
1143 25883 760
New
PragmaticBookshelf
Author Spotlight: Peter Ullrich @PJUllrich Data is at the core of every business, but it is useless if nobody can access and analyze ...
New
PragmaticBookshelf
Develop, deploy, and debug BEAM applications using BEAMOps: a new paradigm that focuses on scalability, fault tolerance, and owning each ...
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
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New