使用 Jason 在 Phoenix 中编码 ObjectId
encoding ObjectId in Phoenix using Jason
我在 Phoenix 有一个频道,我从中获取对象列表 MongoDB:
cursor = Mongo.find(:mongo, "ledgers", %{})
list = Enum.to_list(cursor)
我试着发送这个:
broadcast(socket, "load_ledgers_do", %{payload: list})
这个returns错误:
[error] GenServer #PID<0.3147.0> terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for #BSON.ObjectId<603e521606fc6d39bd3e641d> of type BSON.ObjectId (a struct), Jason.Encoder protocol must always be explicitly implemented.
If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:
@derive {Jason.Encoder, only: [....]}
defstruct ...
It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:
@derive Jason.Encoder
defstruct ...
Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:
Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
Protocol.derive(Jason.Encoder, NameOfTheStruct)
还有一些。
现在回答我的问题,我该如何最好地处理这个错误?
我试过使用 defimpl:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
BSON.ObjectId.encode!(val)
|> Jason.encode()
end
end
但这returns一个错误。确保正确编码 ObjectId 的最佳方法是什么?
编辑错误:
[error] Ranch listener ServerWeb.Endpoint.HTTP had connection process started with :cowboy_clear:start_link/4 at #PID<0.3641.0> exit with reason: {:badarg, [{:erlang, :iolist_size, [[91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [[] | "y"], "\":", "283", 125], 93], 125], 93]], []}, {:cow_ws, :payload_length, 1, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 725]}, {:cow_ws, :frame, 2, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 666]}, {:cowboy_websocket, :websocket_send, 2, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 626]}, {:cowboy_websocket, :handler_call, 6, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 542]}, {:cowboy_http, :loop, 1, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl', line: 254]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}
[error] GenServer #PID<0.3644.0> terminating
** (ArgumentError) argument error
:erlang.iolist_size([91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [[] | "y"], "\":", "283", 125], 93], 125], 93])
(cowlib 2.9.1) /home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl:725: :cow_ws.payload_length/1
(cowlib 2.9.1) /home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl:666: :cow_ws.frame/2
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl:626: :cowboy_websocket.websocket_send/2
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl:542: :cowboy_websocket.handler_call/6
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl:254: :cowboy_http.loop/1
(stdlib 3.13) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message: {:DOWN, #Reference<0.3631150579.3565682692.115779>, :process, #PID<0.3641.0>, {:badarg, [{:erlang, :iolist_size, [[91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [...], ...], 93], 125], 93]], []}, {:cow_ws, :payload_length, 1, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 725]}, {:cow_ws, :frame, 2, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 666]}, {:cowboy_websocket, :websocket_send, 2, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 626]}, {:cowboy_websocket, :handler_call, 6, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 542]}, {:cowboy_http, :loop, 1, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl', line: 254]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}}
State: %Phoenix.Socket{assigns: %{}, channel: ServerWeb.LedgersChannel, channel_pid: #PID<0.3644.0>, endpoint: ServerWeb.Endpoint, handler: ServerWeb.UserSocket, id: nil, join_ref: "141", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: Server.PubSub, ref: nil, serializer: Phoenix.Socket.V2.JSONSerializer, topic: "ledgers", transport: :websocket, transport_pid: #PID<0.3641.0>}
此外,我收到了 defimpl 中编码函数的 linting 警告:
Type mismatch for @callback encode/2 in Jason.Encoder behaviour.
Expected type:
binary()
| maybe_improper_list(
binary() | maybe_improper_list(any(), binary() | []) | byte(),
binary() | []
)
Actual type:
{:error,
%{
:__exception__ => _,
:__struct__ => Jason.EncodeError | Protocol.UndefinedError,
atom() => _
}}
| {:ok, binary()}
我至今没能摆脱它。
这有点棘手,因为二进制 ID 的编码并不总是很好。
您使用的是哪个 Mongo 库?如果您正在使用 mongodb,您可以使用它的 BSON.encode/1
来帮助转换二进制文件,您可以尝试这样的操作:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
val
|> BSON.encode()
|> Base.encode16(case: :lower)
end
end
iex> o = %BSON.ObjectId{value: <<94, 48, 99, 46, 116, 55, 7, 153, 84, 246, 18, 54>>}
#BSON.ObjectId<5e30632e7437079954f61236>
iex> Jason.encode(o)
{:ok, "5e30632e7437079954f61236"}
开始使用了。这似乎是一个正确的解决方案:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
BSON.ObjectId.encode!(val)
|> Jason.encode!()
end
end
少了一个感叹号。
我在 Phoenix 有一个频道,我从中获取对象列表 MongoDB:
cursor = Mongo.find(:mongo, "ledgers", %{})
list = Enum.to_list(cursor)
我试着发送这个:
broadcast(socket, "load_ledgers_do", %{payload: list})
这个returns错误:
[error] GenServer #PID<0.3147.0> terminating
** (Protocol.UndefinedError) protocol Jason.Encoder not implemented for #BSON.ObjectId<603e521606fc6d39bd3e641d> of type BSON.ObjectId (a struct), Jason.Encoder protocol must always be explicitly implemented.
If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON:
@derive {Jason.Encoder, only: [....]}
defstruct ...
It is also possible to encode all fields, although this should be used carefully to avoid accidentally leaking private information when new fields are added:
@derive Jason.Encoder
defstruct ...
Finally, if you don't own the struct you want to encode to JSON, you may use Protocol.derive/3 placed outside of any module:
Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...])
Protocol.derive(Jason.Encoder, NameOfTheStruct)
还有一些。
现在回答我的问题,我该如何最好地处理这个错误?
我试过使用 defimpl:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
BSON.ObjectId.encode!(val)
|> Jason.encode()
end
end
但这returns一个错误。确保正确编码 ObjectId 的最佳方法是什么?
编辑错误:
[error] Ranch listener ServerWeb.Endpoint.HTTP had connection process started with :cowboy_clear:start_link/4 at #PID<0.3641.0> exit with reason: {:badarg, [{:erlang, :iolist_size, [[91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [[] | "y"], "\":", "283", 125], 93], 125], 93]], []}, {:cow_ws, :payload_length, 1, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 725]}, {:cow_ws, :frame, 2, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 666]}, {:cowboy_websocket, :websocket_send, 2, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 626]}, {:cowboy_websocket, :handler_call, 6, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 542]}, {:cowboy_http, :loop, 1, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl', line: 254]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}
[error] GenServer #PID<0.3644.0> terminating
** (ArgumentError) argument error
:erlang.iolist_size([91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [[] | "y"], "\":", "283", 125], 93], 125], 93])
(cowlib 2.9.1) /home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl:725: :cow_ws.payload_length/1
(cowlib 2.9.1) /home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl:666: :cow_ws.frame/2
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl:626: :cowboy_websocket.websocket_send/2
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl:542: :cowboy_websocket.handler_call/6
(cowboy 2.8.0) /home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl:254: :cowboy_http.loop/1
(stdlib 3.13) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message: {:DOWN, #Reference<0.3631150579.3565682692.115779>, :process, #PID<0.3641.0>, {:badarg, [{:erlang, :iolist_size, [[91, "null", 44, "null", 44, [34, [[] | "ledgers"], 34], 44, [34, [[] | "load_ledgers_do"], 34], 44, ["{\"", [[] | "payload"], "\":", [91, ["{\"", [[] | "_id"], "\":", {:ok, "\"603e521606fc6d39bd3e641d\""}, ",\"", [[] | "collection"], "\":", [34, [[] | "ledger_603e521606fc6d39bd3e641d"], 34], ",\"", [[] | "columns"], "\":", "[]", ",\"", [[] | "name"], "\":", [34, [[] | "Ledger1"], 34], ",\"", [[] | "x"], "\":", "427", ",\"", [...], ...], 93], 125], 93]], []}, {:cow_ws, :payload_length, 1, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 725]}, {:cow_ws, :frame, 2, [file: '/home/lars/bdrp/server/deps/cowlib/src/cow_ws.erl', line: 666]}, {:cowboy_websocket, :websocket_send, 2, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 626]}, {:cowboy_websocket, :handler_call, 6, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_websocket.erl', line: 542]}, {:cowboy_http, :loop, 1, [file: '/home/lars/bdrp/server/deps/cowboy/src/cowboy_http.erl', line: 254]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}}
State: %Phoenix.Socket{assigns: %{}, channel: ServerWeb.LedgersChannel, channel_pid: #PID<0.3644.0>, endpoint: ServerWeb.Endpoint, handler: ServerWeb.UserSocket, id: nil, join_ref: "141", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: Server.PubSub, ref: nil, serializer: Phoenix.Socket.V2.JSONSerializer, topic: "ledgers", transport: :websocket, transport_pid: #PID<0.3641.0>}
此外,我收到了 defimpl 中编码函数的 linting 警告:
Type mismatch for @callback encode/2 in Jason.Encoder behaviour.
Expected type:
binary()
| maybe_improper_list(
binary() | maybe_improper_list(any(), binary() | []) | byte(),
binary() | []
)
Actual type:
{:error,
%{
:__exception__ => _,
:__struct__ => Jason.EncodeError | Protocol.UndefinedError,
atom() => _
}}
| {:ok, binary()}
我至今没能摆脱它。
这有点棘手,因为二进制 ID 的编码并不总是很好。
您使用的是哪个 Mongo 库?如果您正在使用 mongodb,您可以使用它的 BSON.encode/1
来帮助转换二进制文件,您可以尝试这样的操作:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
val
|> BSON.encode()
|> Base.encode16(case: :lower)
end
end
iex> o = %BSON.ObjectId{value: <<94, 48, 99, 46, 116, 55, 7, 153, 84, 246, 18, 54>>}
#BSON.ObjectId<5e30632e7437079954f61236>
iex> Jason.encode(o)
{:ok, "5e30632e7437079954f61236"}
开始使用了。这似乎是一个正确的解决方案:
defimpl Jason.Encoder, for: BSON.ObjectId do
def encode(val, _opts \ []) do
BSON.ObjectId.encode!(val)
|> Jason.encode!()
end
end
少了一个感叹号。