将 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 部分除外)
我正在尝试编写一个小应用程序来检索 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.