You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elixir中移除结构体/映射及嵌套结构的__unknown_fields__

移除Elixir结构体中__unknown_fields__字段的优化方案

问题场景

我有如下Elixir结构体,需要从它和嵌套的子结构体中移除所有__unknown_fields__: []字段:

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__: []
}

初始实现及痛点

我写了一段可运行的代码,但方案不够理想:

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

添加日期类型判断是因为递归处理时,DateTime这类日期结构体也会被转成普通映射,不符合预期。

重构后的代码

我已经重构了代码,但仍想寻求更优方案:

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

更简洁的优化方案

可以通过精准匹配需要处理的结构体,避免不必要的类型转换,同时保留DateTime等特殊结构体的原始类型。以下是两种更高效的实现:

方案一:用put_in保留结构体类型

def drop_unknown_fields(%{__unknown_fields__: _} = struct) when is_struct(struct) do
  struct
  |> Map.from_struct()
  |> Map.delete(:__unknown_fields__)
  |> Enum.reduce(struct, fn {key, value}, acc ->
    put_in(acc, [key], drop_unknown_fields(value))
  end)
end

def drop_unknown_fields(%{} = map) do
  map
  |> Enum.reduce(%{}, fn {key, value}, acc ->
    Map.put(acc, key, drop_unknown_fields(value))
  end)
end

def drop_unknown_fields(value), do: value

逻辑说明

  1. 第一个子句仅匹配包含__unknown_fields__的结构体,避免误处理DateTime等其他结构体
  2. 转成映射删除目标字段后,用put_in把处理后的嵌套值放回原结构体,保留原始结构体类型
  3. 递归处理所有嵌套的映射和目标结构体,非目标类型直接返回

方案二:用struct/2重建结构体

def drop_unknown_fields(%{__unknown_fields__: _} = struct) when is_struct(struct) do
  struct
  |> Map.from_struct()
  |> Map.delete(:__unknown_fields__)
  |> Enum.map(fn {key, value} -> {key, drop_unknown_fields(value)} end)
  |> then(&struct(struct, &1))
end

def drop_unknown_fields(%{} = map) do
  map
  |> Enum.reduce(%{}, fn {key, value}, acc ->
    Map.put(acc, key, drop_unknown_fields(value))
  end)
end

def drop_unknown_fields(value), do: value

逻辑说明

  • 核心思路和方案一一致,只是用struct/2直接通过处理后的键值对重建结构体,代码更直观
  • 同样只处理包含目标字段的结构体,完全避免了对DateTime等类型的误转换

内容的提问来源于stack exchange,提问作者Junaid Farooq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:00:54