Hello I have this struct
request = %Model{
id: 4_269_615,
site_id: 3,
user_id: 183_317,
program_id: 174,
type: %ProgramType{
value: :CASHBACK_PROGRAM,
__unknown_fields__: []
},
type_data: %ProgressProgramUser{
id: 1_336_676,
valid_stake: nil,
level: nil,
theo_ggr: nil,
__unknown_fields__: []
},
start_at: ~U[2024-09-05 13:27:20.391727Z],
end_at: nil,
inserted_at: ~U[2024-09-05 13:27:24.152602Z],
program_name: "test cb program 20 min GOLD NFT",
__unknown_fields__: []
}
i am trying to remove the __unknown_fields__: []
from struct as well as nested structs.
the code I am using is working
def drop_unknown_fields(struct) when is_struct(struct) do
struct
|> Map.from_struct()
|> drop_unknown_fields()
end
def drop_unknown_fields(%{} = map) when is_map(map) do
map
|> Enum.reduce(%{}, fn {key, value}, acc ->
updated_value =
case date?(value) do
true -> value
false -> drop_unknown_fields(value)
end
Map.put(acc, key, updated_value)
end)
|> Map.delete(:__unknown_fields__)
end
def drop_unknown_fields(value), do: value
def date?(%DateTime{}), do: true
def date?(%Date{}), do: true
def date?(%NaiveDateTime{}), do: true
def date?(_), do: false
but I think its not right approach to do it. The reason of checking date is: When I try to do nested structs, datatime is a struct as well and it get resolved into a map, which Is not required. So Can you please guide me some optimal way to clean this struct and remove :__unknown_fields__
thank you
Update: I refactored it with code below, but still looking for suggestions.
def drop_unknown_fields(struct) when is_struct(struct) do
struct
|> Map.from_struct()
|> only_consider_unknown_fields(struct)
|> drop_unknown_fields()
end
def drop_unknown_fields(%{} = map) when is_map(map) do
map
|> Enum.reduce(%{}, fn {key, value}, acc ->
updated_value = drop_unknown_fields(value)
Map.put(acc, key, updated_value)
end)
|> Map.delete(:__unknown_fields__)
end
def drop_unknown_fields({:consider, map}), do: drop_unknown_fields(map)
def drop_unknown_fields({:ignore, struct}), do: struct
def drop_unknown_fields(value), do: value
defp only_consider_unknown_fields(map, struct) do
case Map.has_key?(map, :__unknown_fields__) do
true -> {:consider, map}
false -> {:ignore, struct}
end
end
Here's an implementation that I think works like the way you want.
Structs which have that specific key are converted to maps, that specific key is removed, and their values are recursively processed.
Other structs are left as-is.
Non-struct maps then have their values processed and converted back into a map.
Any other value is left as-is.
def drop_unknown_fields(%_{__unknown_fields__: _} = struct) do
struct |> Map.from_struct() |> Map.delete(:__unknown_fields__) |> drop_unknown_fields()
end
def drop_unknown_fields(%_{} = struct), do: struct
def drop_unknown_fields(%{} = map) do
for {k, v} <- map, into: %{}, do: {k, drop_unknown_fields(v)}
end
def drop_unknown_fields(value), do: value
Output:
%{
id: 4269615,
type: %{value: :CASHBACK_PROGRAM},
site_id: 3,
user_id: 183317,
program_id: 174,
type_data: %{id: 1336676, level: nil, valid_stake: nil, theo_ggr: nil},
start_at: ~U[2024-09-05 13:27:20.391727Z],
end_at: nil,
inserted_at: ~U[2024-09-05 13:27:24.152602Z],
program_name: "test cb program 20 min GOLD NFT"
}
Full code:
defmodule Model do
defstruct [
:id,
:site_id,
:user_id,
:program_id,
:type,
:type_data,
:start_at,
:end_at,
:inserted_at,
:program_name,
:__unknown_fields__
]
end
defmodule ProgramType do
defstruct [
:value,
:__unknown_fields__
]
end
defmodule ProgressProgramUser do
defstruct [
:id,
:valid_stake,
:level,
:theo_ggr,
:__unknown_fields__
]
end
defmodule Main do
def main do
request = %Model{
id: 4_269_615,
site_id: 3,
user_id: 183_317,
program_id: 174,
type: %ProgramType{
value: :CASHBACK_PROGRAM,
__unknown_fields__: []
},
type_data: %ProgressProgramUser{
id: 1_336_676,
valid_stake: nil,
level: nil,
theo_ggr: nil,
__unknown_fields__: []
},
start_at: ~U[2024-09-05 13:27:20.391727Z],
end_at: nil,
inserted_at: ~U[2024-09-05 13:27:24.152602Z],
program_name: "test cb program 20 min GOLD NFT",
__unknown_fields__: []
}
request |> drop_unknown_fields() |> IO.inspect()
end
def drop_unknown_fields(%_{__unknown_fields__: _} = struct) do
struct |> Map.from_struct() |> Map.delete(:__unknown_fields__) |> drop_unknown_fields()
end
def drop_unknown_fields(%_{} = struct), do: struct
def drop_unknown_fields(%{} = map) do
for {k, v} <- map, into: %{}, do: {k, drop_unknown_fields(v)}
end
def drop_unknown_fields(value), do: value
end
Main.main()