将 JSON 数据保存到 Zotonic 中的数据库

Saving JSON data to DB in Zotonic

我正在尝试编写一个小应用程序来检索 JSON 文件(它包含一个项目列表,它们都有一些属性),将其内容保存到数据库中,然后稍后显示其中的一些内容.我有 Zotonic 和 运行,生成一些 HTML 没问题。
ATM 我一直在试图弄清楚如何定义自定义资源以及如何从数据库中的 JSON 获取数据。当数据在那里时我应该没问题,文档似乎涵盖了那部分。
我写了一些独立的 erlang 脚本来获取数据,我注意到 Zotonic 有一个用于解码 JSON 的库,所以这部分应该没问题。关于在哪里放置哪些代码或在哪里进一步查看的任何提示?

z_db 模块允许使用以下方法创建自定义 table:

z_db:create_table(Table, Cols, Context).

Table 变量是您的 table 名称,可以是原子或包含单个原子的列表。
Cols 是列定义的列表,由记录定义。当前记录定义(您可以在 include/zotonic.hrl 中找到)是:

-record(column_def, {name, type, length, is_nullable=true, default, primary_key}).

有关记录的更多信息,请参见Erlang docs on records

我在 users/sites/[sitename]/models/m_[sitename].erl 中输入的示例代码:

init(Context) ->
        case z_db:table_exists(?table,Context) of
        false ->
                z_db:create_table(tablename,
                [
                #column_def{name=id, type="serial"},
                #column_def{name=gid, type="integer", is_nullable=false},
                #column_def{name=magnitude, type="real"},
                #column_def{name=depth, type="real"},
                #column_def{name=location, type="character varying"},
                #column_def{name=time, type="integer"},
                #column_def{name=date, type="integer"}
                ], Context);
        true -> ok
        end,
        ok.

注意你指定的记录的选项。我得到的大部分错误是例如从在整数字段上指定长度。

models/m_sitename:init/1 不会在站点启动时被调用。 sitename:init/1 确实被调用,所以我在那里调用 init 函数以确保 table 存在。示例:

init(Context) ->
        m_sitename:init(Context).

由Zotonic自动调用站点的Context变量。您也可以使用 z:c(sitename). 手动获取此变量。所以如果你从其他地方调用 m_sitename:init(Context). 你会这样做:

m_sitename:init(z:c(sitename)).  

接下来,可以通过以下方式在数据库中插入:

z_db:insert(Table, PropList, Context).

其中 Table 又是一个原子或包含表示 table 名称的单个原子的列表。上下文同上
PropList 是一个 属性 列表,它是一个包含由两个元素组成的元组的列表,其中第一个是原子,第二个是其关联的 value/property。示例:

PropList = [
            {row, Value},
            {anotherrow, AnotherValue}
           ].

Table = tablename.
Context = z:c(sitename).
z_db:insert(Table, PropList, Context).

有关 属性 列表的详细信息,请参阅 Erlang docs on Property Lists

=== 依赖项已更新,因此如果您从源代码构建,则不再需要下面的步骤 ===

JSON 部分有点棘手。 Zotonic 包含 mochijson2 并且作为次要依赖项也包含 jiffy。 jiffy 的最新版本包含 jiffy:decode/2 ,它允许您将地图指定为 return 类型。比标准 {struct, {struct, <<"">>}} 怪物更具可读性。要更新到最新版本,请编辑 deps/twerl/rebar.config 中的行

{jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.8.3"}}},

{jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.14.3"}}},

现在运行z:m().Zotonic shell。 (您必须在每次更改代码后执行此操作)。
现在检查 Zotonic shell 是否有 jiffy:decode/2 可以通过键入 jiffy: <tab> 获得,它将显示可用函数列表及其数量。

从互联网检索 JSON 文件 运行:

{ok, {{_, 200, _}, _, Body}} = httpc:request(get, {"url-to-JSON-here", []}, [], [])

这将生成包含内容的变量 Body。有关此电话的更多信息,请参阅 Erlang docs on http client

接下来将 Body 的内容转换为 Erlang 术语:

JsonData = jiffy:decode(Body, [return_maps]).

接下来要做什么很大程度上取决于 JSON 资源的结构。请记住,现在所有内容都是二进制 UTF-8 编码字符串!如果您将 JsonData 打印到屏幕(只需在 Zotonic/Erlang shell 中输入 JsonData.),您将看到很多 #map{<<"key"", <<"Value">>} this.
我的数据是嵌套的,所以我必须像这样提取所需的数据:

[{_,ItemList}|_] = ListData.

这给了我一个地图列表,为了将它们作为单独的项目进行处理,我使用了以下函数:

 get_maps([]) ->
     done; 
 get_maps([First|Rest]) ->
     Map = maps:get(<<"properties">>, First),
     case is_map(Map) of
             true ->
                     map_to_proplist(Map),
                     get_maps(Rest);
             false -> done
             end,
     done; 
get_maps(_) ->
     done.

您可能还记得,z_db:insert/3 函数需要一个 属性 列表来填充行,因此调用 map_to_proplist/1 的目的。此函数的外观完全取决于您的数据的外观,但这里的示例对我有用:

map_to_proplist(Map) ->
        case is_map(Map) of
                true ->
                        {Value1,_}         = string:to_integer(binary_to_list(maps:get(<<"key1">>, Map))),
                        {Value2,_}   = string:to_float(binary_to_list(maps:get(<<"key2">>, Map))),
                        {Value3,_}       = string:to_float(binary_to_list(maps:get(<<"key3">>, Map))),
                        Value4        = binary_to_list(maps:get(<<"key4">>, Map)),
                        {Value5,_}        = string:to_integer(binary_to_list(maps:get(<<"key5">>, Map))),
                        {Value6,_}        = string:to_integer(binary_to_list(maps:get(<<"key6">>, Map))),
                        PropList = [{rowname1, Value1}, {rowname2, Value2}, {rowname3, Value3}, {rowname4, Value4}, {rowname5, Value5}, {rowname6, Value6}],
                        m_sitename:insert_items(PropList,z:c(sitename)),
                        ok;
                false ->
                        ok
                end.

请参阅 documentation on string:to_list/1 了解为什么在转换时需要元组。对 m_sitename:insert_items(PropList,z:c(sitename)) 的调用调用了 models/m_sitename.erl 中的 z_db:insert/3 但包含在捕获中:

insert_items(PropList,Context) ->
        (catch z_db:insert(?table, PropList, Context)).

好吧,很长 post 但如果您正在寻找这个答案,这应该会让您振作起来 运行ning。

以上是在 Erlang/OTP 18.
上使用 Zotonic 0.13.2 完成的 我的 post in the Zotonic Developers group.

的 repost(JSON 部分除外)