【问题标题】:Elixir - convert http response json to ecto.schema objectElixir - 将 http 响应 json 转换为 ecto.schema 对象
【发布时间】:2020-04-14 09:14:38
【问题描述】:

我是长生不老药的新手。我有一个 Ecto 架构

  defmodule MyScoreSchema do
  use Ecto.Schema
  import Ecto.Changeset

  schema "historical_extra_fuels" do
    field :average, :float
    field :count, :float
    field :percent, :float
    field :name, :string
  end


  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:average, :count, :percent])
    |> validate_required([])
  end
end

和父架构

defmodule OverallScore do
  use Ecto.Schema
  import Ecto.Changeset

  schema "OverallScore" do
    field :avg_pass, :float
    field :avg_fail, :float
    field :total_students, :float
    embeds_many :my_score_schema, MyScoreSchema
  end
  @required_fields ~w[]a
  @optional_fields ~w[avg_pass, avg_fail, total_students ]a


  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @optional_fields, required: false )
    |> cast_embed(:my_score_schema, required: false)
  end
end

并且有一个 HTTP REST API http://localhost:8080/getScoreData 提供数据

{
      "avgPass": 85.55,
      "avgFail": 14.45,
      "totalStudents": 80.0,
      "myScoreSchema": [
        {
          "average": 80.0,
          "count": 8.0,
          "percent": 80.0,
          "name": "John"
        },
        {
          "average": 90.0,
          "count": 8.0,
          "percent": 90.0,
          "name": "Cena"
        },
        {
          "average": 80.0,
          "count": 8.0,
          "percent": 80.0,
          "name": "Sunny"
        },
        {
          "average": 70.0,
          "count": 8.0,
          "percent": 70.0,
          "name": "Michael"
        }
      ]
    }

和代码

  url = "http://localhost:8080/getScoreData"
   Logger.info("the url is #{url}")
   case HTTPoison.get(url) do
     {:ok, %{status_code: 200, body: body}} ->
       overall_score = Jason.decode!(body, as: [%OverallScore{}])
       {:ok, overall_score}
   end

这在某种程度上可行并且不会出错,但结果是一些 struct 而不是真正的 OverallScore ecto schema object

【问题讨论】:

  • 结果如何?你收到一个%OverallScore{},为什么要尝试编码到列表中?
  • 您在哪里发现Jason.decode!/1 接受as: 选项?

标签: json elixir ecto elixir-poison httpoison


【解决方案1】:

我建议不要使用“as: [%OverallScore{}]”语法,而是使用模型中已有的变更集。这看起来像这样:

  url = "http://localhost:8080/getScoreData"
   Logger.info("the url is #{url}")
   case HTTPoison.get(url) do
     {:ok, %{status_code: 200, body: body}} ->
       response = Jason.decode!(body)
       overall_score = OverallScore.changeset(%OverallScore{}, response)
       {:ok, overall_score}
   end

变更集通常是将对象放入 ecto 结构的最佳方式,因为它们将正确地运行您的变更集验证。这样做还有一个额外的好处,就是它会在“响应”字段中删除不在变更集的强制转换调用中的任何内容,而不会出现任何错误。您还可以快速检查它是否有效,如果有效,您可以使用 ecto repo 将其插入数据库。

【讨论】:

  • 这里的overall_score#Ecto.Changeset<action: nil, changes: %,而不是OverallScore 对象。我严格要返回OverallScore 对象而不是Changeset。我认为这将仍然是一个变更集对象,除非我做我不需要的 Repo.insert
  • @veer7 如果您只想构建结构而不是使用变更集,那么您将需要使用此处所示的“Kernel.struct/2”函数 - stackoverflow.com/a/41980865/2265722。如链接的答案中所述,您首先需要将映射从字符串键转换为原子键。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-17
  • 2017-09-13
  • 2018-07-26
  • 1970-01-01
  • 2019-11-04
  • 2016-02-21
  • 1970-01-01
相关资源
最近更新 更多