Skip to content

Instantly share code, notes, and snippets.

@Slavenin
Last active October 20, 2022 06:52
Show Gist options
  • Save Slavenin/598e3059ffad99565e4565bdf207dc78 to your computer and use it in GitHub Desktop.
Save Slavenin/598e3059ffad99565e4565bdf207dc78 to your computer and use it in GitHub Desktop.
Implementation protocol Jason.Encoder with a group of fields support
defmodule MyApp.Serializer.Fields do
defmacro __using__(fields) do
quote do
@json_group_fields unquote(fields)
def get_fields_for_group(group) do
group = MapSet.new(group)
Enum.reduce(@json_group_fields, [], fn {f, g}, acc ->
g = MapSet.new(g)
if has_group?(group, g), do: [f | acc], else: acc
end)
end
def has_group?(group, g) do
group
|> MapSet.intersection(g)
|> MapSet.size() > 0
end
end
end
end
defmodule MyApp.Serializer.GroupFieldEncoder do
defstruct data: nil, groups: [], exclude: [:__meta__, :__struct__]
def wrap(data, groups, exclude) do
%__MODULE__{
data: data,
groups: groups,
exclude: exclude
}
end
end
defimpl Jason.Encoder, for: Opc.Schemas.Serializer.GroupFieldEncoder do
alias Opc.Schemas.Serializer.GroupFieldEncoder
@spec encode(Opc.Schemas.Serializer.GroupFieldEncoder.t(), any) :: iodata
def encode(%GroupFieldEncoder{data: %_{} = item, groups: groups, exclude: exclude}, opts) do
encode_one(item, groups, exclude, opts)
|> Jason.Encode.map(opts)
end
def encode(%GroupFieldEncoder{data: items, groups: groups, exclude: exclude}, opts)
when is_list(items) do
Enum.map(items, &encode_one(&1, groups, exclude, opts))
|> Jason.Encode.list(opts)
end
def encode(%GroupFieldEncoder{data: item, groups: groups, exclude: exclude}, opts)
when is_map(item) do
encode_item(item, exclude, groups, opts)
|> Jason.Encode.map(opts)
end
defp encode_one(%module{} = item, groups, exclude, opts) do
if function_exported?(module, :get_fields_for_group, 1) do
Map.from_struct(item)
|> Map.take(module.get_fields_for_group(groups |> MapSet.new()))
else
item
end
|> encode_item(exclude, groups, opts)
end
defp encode_one(items, groups, exclude, opts) when is_list(items) do
Enum.map(items, &encode_one(&1, groups, exclude, opts))
|> Jason.Encode.list(opts)
end
defp encode_one(item, groups, exclude, opts) when is_map(item) do
item
|> encode_item(exclude, groups, opts)
end
defp encode_item(%_{} = item, _exclude, _groups, _opts), do: item
defp encode_item(item, exclude, groups, opts) do
item
|> Enum.filter(fn {k, _v} -> not (k in exclude) end)
|> Enum.map(fn
{k, %_{} = v} -> {k, encode_one(v, groups, exclude, opts)}
{k, v} when is_list(v) -> {k, Enum.map(v, &encode_one(&1, groups, exclude, opts))}
{k, v} -> {k, v}
end)
|> Enum.into(%{})
end
end
user = Repo.get(id)
%MyApp.Serializer.GroupFieldEncoder{data: user, groups: [:show]}
|> Jason.encode()
defmodule MyApp.UserSchema do
use Opc.Schemas.Serializer.Fields, %{
id: [:list, :show],
surname: [:show],
name: [:list, :show],
patronymic: [:list, :show]
}
# define you user schema
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment