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
2
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()
0