Json in Elixir

Here’s the translation of the JSON example from Go to Elixir, along with explanations in Markdown format suitable for Hugo:

defmodule JsonExample do
  # We'll use these two structs to demonstrate encoding and
  # decoding of custom types below.
  defmodule Response1 do
    defstruct [:page, :fruits]
  end

  defmodule Response2 do
    @derive Jason.Encoder
    defstruct [:page, :fruits]
  end

  def run do
    # First we'll look at encoding basic data types to
    # JSON strings. Here are some examples for atomic
    # values.
    bol_b = Jason.encode!(true)
    IO.puts(bol_b)

    int_b = Jason.encode!(1)
    IO.puts(int_b)

    flt_b = Jason.encode!(2.34)
    IO.puts(flt_b)

    str_b = Jason.encode!("gopher")
    IO.puts(str_b)

    # And here are some for lists and maps, which encode
    # to JSON arrays and objects as you'd expect.
    slc_d = ["apple", "peach", "pear"]
    slc_b = Jason.encode!(slc_d)
    IO.puts(slc_b)

    map_d = %{"apple" => 5, "lettuce" => 7}
    map_b = Jason.encode!(map_d)
    IO.puts(map_b)

    # The Jason library can automatically encode your
    # custom data types. It will only include struct fields
    # in the encoded output and will by default
    # use those names as the JSON keys.
    res1_d = %Response1{page: 1, fruits: ["apple", "peach", "pear"]}
    res1_b = Jason.encode!(res1_d)
    IO.puts(res1_b)

    # You can use @derive Jason.Encoder on struct definitions
    # to customize the encoded JSON. Check the definition of
    # Response2 above to see an example of such usage.
    res2_d = %Response2{page: 1, fruits: ["apple", "peach", "pear"]}
    res2_b = Jason.encode!(res2_d)
    IO.puts(res2_b)

    # Now let's look at decoding JSON data into Elixir
    # values. Here's an example for a generic data
    # structure.
    byt = ~s({"num":6.13,"strs":["a","b"]})

    # We need to decode the JSON string. This will return
    # a map with string keys.
    {:ok, dat} = Jason.decode(byt)
    IO.inspect(dat)

    # In order to use the values in the decoded map,
    # we can access them directly using the string keys.
    num = dat["num"]
    IO.puts(num)

    # Accessing nested data is straightforward in Elixir.
    str1 = List.first(dat["strs"])
    IO.puts(str1)

    # We can also decode JSON into custom data types.
    # This has the advantages of adding additional
    # type-safety to our programs and eliminating the
    # need for type assertions when accessing the decoded
    # data.
    str = ~s({"page": 1, "fruits": ["apple", "peach"]})
    {:ok, res} = Jason.decode(str, keys: :atoms)
    res = struct(Response2, res)
    IO.inspect(res)
    IO.puts(List.first(res.fruits))

    # In the examples above we always used strings as
    # intermediates between the data and JSON representation.
    # We can also stream JSON encodings directly to IO devices
    # like standard output or even files.
    d = %{"apple" => 5, "lettuce" => 7}
    Jason.encode!(d) |> IO.puts()
  end
end

JsonExample.run()

To run this Elixir program, you would typically save it in a file (e.g., json_example.exs) and then run it using:

$ elixir json_example.exs
true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"page":1,"fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
%{"num" => 6.13, "strs" => ["a", "b"]}
6.13
a
%JsonExample.Response2{page: 1, fruits: ["apple", "peach"]}
apple
{"apple":5,"lettuce":7}

This example demonstrates JSON encoding and decoding in Elixir using the Jason library, which is a popular JSON library for Elixir. It covers encoding of basic types, custom structs, and decoding JSON into both maps and custom structs.

Note that Elixir uses defmodule and defstruct instead of Go’s type for defining custom types. Also, Elixir’s pattern matching and pipe operator (|>) are used to handle function results and chaining operations.

The @derive Jason.Encoder attribute on Response2 is similar to using struct tags in Go for customizing JSON encoding. In Elixir, this tells the Jason library to automatically derive an encoder for the struct.

Remember to add {:jason, "~> 1.2"} to your project’s dependencies in mix.exs if you’re using this in a Mix project.