0

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

4

1 回答 1

2

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

z_db:create_table(Table, Cols, Context).

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

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

有关记录的更多信息,请参阅有关记录的Erlang 文档

我放在 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 函数以确保表存在。例子:

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 又是一个原子或包含表示表名的单个原子的列表。上下文与上述相同。
PropList 是一个属性列表,它是一个包含由两个元素组成的元组的列表,其中第一个是原子,第二个是其关联的值/属性。例子:

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

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

有关属性列表的更多信息,请参阅属性列表上的Erlang 文档。

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

JSON 部分有点棘手。Zotonic 包含 mochijson2 和jiffy作为次要依赖项。最新版本的 jiffy 包含 jiffy:decode/2 ,它允许您将映射指定为返回类型。比标准{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>,它会显示可用函数的列表及其数量。

要从 Internet 检索 JSON 文件,请运行:

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

这将产生带有内容的变量 Body。有关此调用的更多信息,请参阅http 客户端上的 Erlang 文档。

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

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

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

[{_,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.

请参阅有关 string:to_list/1 的文档,了解为什么在转换时需要元组。调用m_sitename:insert_items(PropList,z:c(sitename))z_db:insert/3 inmodels/m_sitename.erl但包含在一个 catch 中:

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

好的,很长的帖子,但是如果您正在寻找这个答案,这应该可以帮助您启动并运行。

以上是在 Erlang/OTP 18 上使用 Zotonic 0.13.2 完成的。我在 Zotonic Developers group 中的
帖子的转贴(JSON 部分除外)。

于 2015-09-08T11:24:33.273 回答