Render json easier with relationship and custom data rendering
The package can be installed
by adding json_view
to your list of dependencies in mix.exs
:
def deps do
[
{:json_view, "~> 0.2.0"}
]
end
Documentation can be found at https://hexdocs.pm/json_view.
Normally, you query data from database then render to JSON and return to client, and you might want to:
- Keep the original value
- Return value in a new format, or return some calculated data
- Render relationships that defined by Ecto schema
JsonView helps to render json data easier by support relationship and custom render data. Most of the time you may want to add it to your view:
def view do
quote do
...
use JsonView
...
end
end
Or you can use it directly on your view
defmodule MyApp.UserView do
use JsonView
def render("user.json", %{user: user}) do
render_json(user, [:first_name, :last_name, :vatar], [], [])
end
end
defmodule MyApp.PostView do
use JsonView
# define which fields return without modifying
@fields [:title, :content, :excerpt, :cover]
# define which fields that need to format or calculate, you have to define `render_field/2` below
@custom_fields [:like_count]
# define which view used to render relationship
@relationships [author: MyApp.UserView]
def render("post.json", %{post: post}) do
# 1st way if `use JsonView`
render_json(post, @fields, @custom_fields, @relationships)
end
def render_field(:like_count, item) do
# load like_count from some where
end
end
And then use it
post = %Post{
title: "Hello JsonView",
excerpt: "Now you can render Json easier",
content: "Install and put it to work",
cover: nil,
inserted_at: ~N[2021-07-05 00:00:00],
updated_at: ~N[2021-07-09 00:00:00],
author: %User{
first_name: "Daniel",
last_name: "James",
email: "[email protected]",
avatar: nil,
inserted_at: ~N[2021-06-30 00:00:00]
updated_at: ~N[2021-07-02 00:00:00]
}
}
MyApp.PostView.render("post.json", %{post: post})
# or invoke from PostController
render(conn, "post.json", post: post)
This is the result that you can use to return from PhoenixController
%{
title: "Hello JsonView",
excerpt: "Now you can render Json easier",
content: "Install and put it to work",
cover: nil,
like_count: nil,
author: %{
first_name: "Daniel",
last_name: "James"
}
}
-
Custom field
@custom_fields [:like_count] # this invokes `render(:like_count, post)` @custom_fields [like_count: &my_function/1] # this invokes `my_function.(post)`
-
Relationship
@relationships [author: MyApp.UserView] # this invokes `MyApp.UserView.render("user.json", %{user: user})` @relationships [author: {MyApp.UserView, "basic_profile.json"}] # this invokes `MyApp.UserView.render("basic_profile.json", %{user: user})`
JsonView
render fields
-> custom_fields
-> relationships
. If they define same field, then the latter will override the prior
You can pass a list of default fields
and/or custom_fields
as options to use JsonView
. These fields then merged to fields
and custom_fields
before rendering data each time you invoke render_json
use JsonView, fields: [:id, :updated_at], custom_fields: [inserted_at: &to_local_time/2]
You can pass a function to process data after JsonView
completes rendering like this:
use JsonView, after_render: &convert_all_datetime_to_local/1
def convert_all_datetime_to_local(data) do
Enum.map(data, fn {k, v} ->
v =
case v do
%NaiveDateTime{} -> to_local_datetime(v)
_ -> v
end
{k, v}
end)
|> Enum.into(%{})
end