Search code examples
elixirphoenix-frameworkecto

Poison Encode encoding nested models


Sup,

I'm trying to encode some nested Ecto Models but i have encountered a problem. I realised that i can't encode unloaded model and i want to have possibility of selecting only one structure without loading rest and at the same time having possibility of selecting whole tree.

I have used method with defimpl, example:

defimpl Poison.Encoder, for: Hangman.MasterCat do
    def encode(page, _options) do
      %{
        id: page.id,
        name: page.name,
        categories: page.categories
      } |> Poison.Encoder.encode([])
    end
  end
defimpl Poison.Encoder, for: Hangman.Category do
    def encode(category, _options) do
      IO.inspect(category.words)
      %{
        id: category.id,
        name: category.name,
        words: category.words
      } |> Poison.Encoder.encode([])
    end
  end
defimpl Poison.Encoder, for: Hangman.Word do
    def encode(page, _options) do
      %{
        content: page.content
      } |> Poison.Encoder.encode([])
    end
  end

This works for whole structure but it doesn't work for selecting only MasterCats and it gives such error.

cannot encode association :categories from Hangman.MasterCat to JSON because the association was not loaded. Please make sure you have preloaded the association or remove it from the data to be encoded

Solution

  • You need to define the same encoder for categories too and so on. In fact, there is an easier to do this, if you are using latest Poison, you can write:

    defmodule Hangman.MasterCat do
      use Ecto.Model
    
      @derive {Poison.Encoder, only: [:id, :name, :categories]}
      schema "..." do
    

    This way, Elixir takes care of automatically implementing the protocol for you. As long as you derive the encoder for the types you care, you should be good. For complex cases, explicitly implementing the protocol, as you did, is the way to go.