#TIL #elixirlang 구조체(struct)를 jason을 사용해 인코딩하려면

csv로 저장하려고 했는데, jason 라이브러리를 사용하면 직렬화(serialization), 역직렬화(deserialization)가 편해서 json 포멧을 사용한다.

defmodule Data do
  defstruct [:a, :b, :c, :d]
end

Data 구조체를 만들고 이걸 json으로 인코딩해본다.

content = %Data{a: 1, b: 2, c: 3, d: 4}
Jason.encode!(content, pretty: true)
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for %Data{a: 1, b: 2, c: 3, d: 4} of type Data (a struct), Jason.Encoder protocol must always be explicitly implemented.

If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:

    @derive {Jason.Encoder, only: [....]}
    defstruct ...

It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:

    @derive Jason.Encoder
    defstruct ...

Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:

    Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
    Protocol.derive(Jason.Encoder, NameOfTheStruct)
. This protocol is implemented for the following type(s): Any, Atom, BitString, Date, DateTime, Decimal, Float, Integer, Jason.Fragment, List, Map, NaiveDateTime, Time
    (jason) lib/jason.ex:150: Jason.encode!/2

Data 구조체에 대한 Jason.Encoder 프로토콜이 구현 안 됐다며 에러가 난다. 도움말이 너무 친절해 그대로 따라 하면 된다.

defimpl Jason.Encoder, for: Data do
  def encode(value, opts) do
    Jason.Encode.map(Map.take(value, [:a, :b, :c, :d]), opts)
  end
end

이렇게 직접 구현하던가

defmodule Data do
  @derive Jason.Encoder
  defstruct [:a, :b, :c, :d]
end

@derived 속성을 사용해 기본 구현을 사용한다고 알려주면 된다.

iex> Jason.encode!(content, pretty: true)
"{\n  \"a\": 1,\n  \"b\": 2,\n  \"c\": 3,\n  \"d\": 4\n}"

구조체도 json으로 인코딩이 잘 된다.

@derived 속성을 사용하면 __deriving__ 콜백 함수가 호출된다. :only, :except 옵션을 사용하지 않으면 __struct__ 키만 빼고 인코딩한다. 프로토콜을 구현할 때, __deriving__ 콜백을 구현해놓으면 사용하는 사람이 편하다.

encode.ex 파일 구현을 보면 Date, Time, NaiveDateTime, DateTime 구조체는 미리 정의해놨다. 사용자가 의도하지 않는 인코딩을 방지하려고 구조체를 인코딩하려고 하면 에러를 일으킨다. 의도한 게 맞다면 구현하기 쉽게 @derived 속성을 사용하게 구현을 해놨다. 의도하지 않은 사용은 방지하고 원한다면 쉽게 구현할 수 있게 디자인했다. 정석처럼 잘해놨다.

Feedback plz <3 @ohyecloudy, ohyecloudy@gmail.com

A Random Post