在Zotonic中将JSON数据保存到DB

时间:2015-09-03 14:16:44

标签: json postgresql erlang zotonic

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

1 个答案:

答案 0 :(得分: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 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/1sitename:init/1会被调用,所以我在那里调用init函数来确保表存在。例如:

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

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

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

接下来,可以使用以下命令在DB中插入:

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 docs on Property Lists

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

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"}}},

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

从互联网运行中检索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">>}这个。
我的数据是嵌套的,所以我不得不提取所需的数据:

[{_,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,但包含在catch中:

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

好的,相当长的帖子但如果您正在寻找这个答案,这应该可以帮助您正常运行。

以上是在Erlang / OTP 18上用Zotonic 0.13.2完成的。
我的帖子in the Zotonic Developers group的重新发布(JSON部分除外)。