Closed Beisenbek closed 6 years ago
Sample result set
[
{
"to": "UserA@{host}",
"timestamp": 1528283702183,
"id": "DeGu9-20159",
"from": "UserB@{host}",
"body": "test message"
}
]
Hi @Beisenbek,
Thanks for trying the HTTP API! We implemented basic functionality over HTTP and are open to extend it. How would you see the extended result? Do you expect the full xml stanzas in the response or more keys in the JSON object?
Hi @michalwski It seems to me that it'll better to get full xml stanzas in the response and more keys in the JSON object. In my case I want to put messages through active tcp connection and get MAM messages through REST API. So, there is problem in getting identical result for json based REST API and standard xml based MAM stanza.
I think we could start with the full xml added to the JSON object under xml
key like below:
[
{
"to": "UserA@{host}",
"timestamp": 1528283702183,
"id": "DeGu9-20159",
"from": "UserB@{host}",
"body": "test message",
"xml": "<message id='some-id' to='UserA@{host}`><body>test message</body><other/></message>"
}
]
The place in the code which generates the result is here: https://github.com/esl/MongooseIM/blob/33db205313b412e89fad3a3217b7a67808acfbb6/src/mongoose_client_api_messages.erl#L108-L114 would you be able to send us a PR? We are about to start new sprint so there will probably be no time to make the change in next 2 weeks by us.
ok, I'll try send PR as quick as possible.
Hi @michalwski
I've faced with problem. I got error invalid_ejson
when tried to convert xml object to json.
My steps:
X1
from Msg
object with exml:to_binary
X2
from X1
. So X2
equals to
{<<"message">>,
[
[
{<<"@xml:lang">>,<<"en">>},
{<<"@to">>,<<"UserA">>},
{<<"@id">>,<<"1V4jk-18041">>},
{<<"@type">>,<<"chat">>},
{<<"body">>,
[
[
{<<"@xml:lang">>,<<"en_US">>},
{<<"#text">>,<<208,159,209,128,208,184,208,178,208,181,209,130,33,32,92,110,32,208,154,208,176,208,186,32,208,180,208,181,208,187,208,176,63>>}
]
]
},
{<<"properties">>,
[
[
{<<"@xmlns">>,<<"http://www.jivesoftware.com/xmlns/xmpp/properties">>},
{<<"property">>,[{<<"name">>,[{<<"#text">>,<<"chatVersion">>}]},{<<"value">>,[[{<<"@type">>,<<"string">>},{<<"#text">>,<<"4">>}]]}]},
{<<"property">>,[{<<"name">>,[{<<"#text">>,<<"prevMessageId">>}]},{<<"value">>,[[{<<"@type">>,<<"string">>},{<<"#text">>,<<"mB6D0-33">>}]]}]}
]
]
}
]
]}
X2
to json with jiffy:encode
. Any help?Parser code taken from here: https://stackoverflow.com/a/43151721:
convert(XML) when erlang:is_binary(XML) ->
{ok, XMLEl} = exml:parse(XML),
V = convert2(XMLEl),
jiffy:encode({[V]}).
convert2(#xmlel{name = Name
,attrs = []
,children = Children}) ->
{Name, convert_children(Children,[])};
convert2(#xmlel{name = Name
,attrs = []
,children = [{xmlcdata, Data}]}) ->
{Name, Data};
convert2(#xmlel{name = Name
,attrs = Attrs
,children = Children}) ->
{Name, [convert_attrs(Attrs,[]) ++ convert_children(Children,[])]}.
convert_attrs([V|T], Acc) ->
convert_attrs(T, Acc ++ [convert_attr(V)]);
convert_attrs([], Acc) -> Acc.
convert_attr({Attr, Value}) ->
{<<$@, Attr/binary>>, Value}.
convert_children([V|T], Acc) ->
convert_children(T, Acc ++ [convert_child(V)]);
convert_children([], Acc) -> Acc.
convert_child({xmlcdata, Data}) ->
{<<"#text">>, Data};
convert_child(#xmlel{}=XMLEl) ->
convert2(XMLEl).
Right, mapping XMPP's message stanza to a valid JSON object may not be possible without dropping some parts. That's why I suggested to add the XML String to the response. I know mixing XML with JSON is not very elegant way. On the other hand this gives you what you need in the easiest way.
Regarding you approach, and the parser you took from stack overflow I think you don't have to call exml:to_binary
because the first thing the parser does is exml:parse
(and you already have that in the Msg
var).
What's the error returned by jiffy:encode
? maybe it gives some hints where is the problem.
I agree that adding XML String to reponse is fastest way. I've implemented it already, but I'm feeling not comfortable with it.
Thanks for advice with exml:to_binary
. Is it possible by some way to get from Msg
var an erlang map
?
Error from jiffy:encode
:
2018-06-19 11:37:55.594 [error] emulator Error in process <0.1544.0> on node mongooseim@localhost with exit value:
{{nocatch,[{reason,{error,{invalid_ejson,{<<"@xml:lang">>,<<"en">>}}}},{mfa,{mongoose_client_api_messages,to_json,2}},{stacktrace,[{jiffy,encode,2,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/jiffy/src/jiffy.erl"},{line,97}]},{mongoose_client_api_messages,encode,2,[{file,"/opt/xmpp/MongooseIM/_build/prod/lib/mongooseim/src/mongoose_client_api_messages.erl"},{line,122}]},{mongoose_client_api_messages,'-maybe_to_json_with_jid/4-lc$^0/1-0-',1,[{file,"/opt/xmpp/MongooseIM/_build/prod/lib/mongooseim/src/mongoose_client_api_messages.erl"},{line,76}]},{mongoose_client_api_messages,maybe_to_json_with_jid,4,[{file,"/opt/xmpp/MongooseIM/_build/prod/lib/mongooseim/src/mongoose_client_api_messages.erl"},{line,76}]},{cowboy_rest,call,3,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/cowboy/src/cowboy_rest.erl"},{line,976}]},{cowboy_rest,set_resp_body,2,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/cowboy/src/cowboy_rest.erl"},{line,858}]},{cowboy_protocol,execute,4,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/cowboy/src/cowboy_protocol.erl"},{line,442}]}]},{req,[{socket,#Port<0.18489>},{transport,ranch_tcp},{connection,keepalive},{pid,<0.1544.0>},{method,<<"GET">>},{version,'HTTP/1.1'},{peer,{{46,19,47,34},32832}},{host,<<"{host}">>},{host_info,undefined},{port,8089},{path,<<"/api/messages/UserA@{host}">>},{path_info,undefined},{qs,<<"limit=1">>},{qs_vals,undefined},{bindings,[{with,<<"UserA@{host}">>}]},{headers,[{<<"cache-control">>,<<"no-cache">>},{<<"authorization">>,<<"Basic ABCDE=">>},{<<"user-agent">>,<<"PostmanRuntime/7.1.1">>},{<<"accept">>,<<"*/*">>},{<<"host">>,<<"{host}:8089">>},{<<"accept-encoding">>,<<"gzip, deflate">>},{<<"connection">>,<<"keep-alive">>}]},{p_headers,[{<<"if-modified-since">>,undefined},{<<"if-none-match">>,undefined},{<<"if-unmodified-since">>,undefined},{<<"if-match">>,undefined},{<<"accept">>,[{{<<"*">>,<<"*">>,[]},1000,[]}]},{<<"authorization">>,{<<"basic">>,{<<"UserB@{host}">>,<<"a0818e41-aa42-4b64-b5b9-46784590c01d">>}}},{<<"connection">>,[<<"keep-alive">>]}]},{cookies,undefined},{meta,[{media_type,{<<"application">>,<<"json">>,[]}},{charset,undefined}]},{body_state,waiting},{buffer,<<>>},{multipart,undefined},{resp_compress,true},{resp_state,waiting},{resp_headers,[{<<"content-type">>,[<<"application">>,<<"/">>,<<"json">>,<<>>]}]},{resp_body,<<>>},{onresponse,undefined}]},{state,#{jid => {jid,<<"UserB">>,<<"{host}">>,<<>>,<<"UserB">>,<<"{host}">>,<<>>},user => <<"UserB@{host}">>}}]},[{cowboy_rest,set_resp_body,2,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/cowboy/src/cowboy_rest.erl"},{line,858}]},{cowboy_protocol,execute,4,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/cowboy/src/cowboy_protocol.erl"},{line,442}]}]}
2018-06-19 11:37:55.594 [error] <0.1544.0> Ranch listener 'ejabberd_cowboy_0.0.0.0_8089' terminated with reason: {nocatch,[{reason,{error,{invalid_ejson,{<<"@xml:lang">>,<<"en">>}}}},{mfa,{mongoose_client_api_messages,to_json,2}},{stacktrace,[{jiffy,encode,2,[{file,"/opt/xmpp/MongooseIM/_build/default/lib/jiffy/src/jiffy.erl"},{line,97}]},{mongoose_client_api_messages,encode,2,[{file,"/opt/xmpp/MongooseIM/_build/prod/lib/mongooseim/src/mongoose_client_api_messages.erl"},{line,122}]},{mongoose_client_api_messages,'-maybe_to_json_with_jid/4-lc$^0/1-0-',1,[{file,"/opt/xmpp/MongooseIM/_build/prod/lib/mong..."},...]},...]},...]} in cowboy_rest:set_resp_body/2 line 858```
Is it possible by some way to get from Msg var an erlang map?
No, currently there is no such an API. I know that be useful in this case.
Regarding the error from jiffy:encode
, I think that it's not able to encode the key @xml:lang
. I'm not sure but :
is probably not a valid character in JSON key.
I checked for key @xml:lang
- removing spec char :
doesn't affect to error. The propblem was in parser. After little refactoring problem with ejson was solved:
convert(XML) ->
{ok, XMLEl} = exml:parse(XML),
V = {[convert2(XMLEl)]},
jiffy:encode(V).
convert2(#xmlel{name = Name
,attrs = []
,children = [{xmlcdata, Data}]}) ->
{Name, Data};
convert2(#xmlel{name = Name
,attrs = Attrs
,children = Children}) ->
{Name, {convert_attrs(Attrs) ++ convert_children(Children)}}.
convert_attrs(Attrs) ->
convert_attrs(Attrs,[]).
convert_attrs([Attr|Attrs1], Attrs2) ->
convert_attrs(Attrs1, [convert_attr(Attr)|Attrs2]);
convert_attrs([], Attrs2) ->
lists:reverse(Attrs2).
convert_attr({Attr, Value}) ->
{<<$@, Attr/binary>>, Value}.
convert_children(Children) ->
convert_children(Children, []).
convert_children([Child|Children1], Children2) ->
convert_children(Children1, [convert_child(Child)|Children2]);
convert_children([], Children2) ->
lists:reverse(Children2).
convert_child({xmlcdata, Data}) ->
{<<"#text">>, Data};
convert_child(#xmlel{}=XMLEl) ->
convert2(XMLEl).
For getting erlang map
, i tried to use jiffy:decode
with return_maps option
31> K = {[{<<"message">>,{[{<<"@xml:lang">>,<<"en">>},{<<"@to">>,<<"UserA">>},{<<"@id">>,<<"1V4jk-18041">>},{<<"@type">>,<<"chat">>},{<<"body">>,{[{<<"@xml:lang">>,<<"en_US">>},{<<"#text">>,<<208,159,209,128,208,184,208,178,208,181,209,130,33,32,92,110,32,208,154,208,176,208,186,32,208,180,208,181,208,187,208,176,63>>}]}},{<<"properties">>,{[{<<"@xmlns">>,<<"http://www.jivesoftware.com/xmlns/xmpp/properties">>},{<<"property">>,{[{<<"name">>,<<"chatVersion">>},{<<"value">>,{[{<<"@type">>,<<"string">>},{<<"#text">>,<<"4">>}]}}]}},
31> {<<"property">>,{[{<<"name">>,<<"prevMessageId">>},{<<"value">>,{[{<<"@type">>,<<"string">>},{<<"#text">>,<<"mB6D0-33">>}]}}]}}]}}]}}]}.
{[{<<"message">>,
{[{<<"@xml:lang">>,<<"en">>},
{<<"@to">>,<<"UserA">>},
{<<"@id">>,<<"1V4jk-18041">>},
{<<"@type">>,<<"chat">>},
{<<"body">>,
{[{<<"@xml:lang">>,<<"en_US">>},
{<<"#text">>,
<<208,159,209,128,208,184,208,178,208,181,209,130,...>>}]}},
{<<"properties">>,
{[{<<"@xmlns">>,
<<"http://www.jivesoftware.com/xmlns/xmpp/propertie"...>>},
{<<"property">>,
{[{<<"name">>,<<"chatVersion">>},
{<<"value">>,
{[{<<"@type">>,<<"string">>},{<<"#text">>,<<"4">>}]}}]}},
{<<"property">>,
{[{<<"name">>,<<"prevMessageId">>},
{<<"value">>,
{[{<<"@type">>,<<"stri"...>>},
{<<"#tex"...>>,<<...>>}]}}]}}]}}]}}]}
34> L = jiffy:encode(K).
<<"{\"message\":{\"@xml:lang\":\"en\",\"@to\":\"UserA\",\"@id\":\"1V4jk-18041\",\"@type\":\"chat\",\"body\":{\"@xml:lang\":\"en_US\",\"#text\":\""...>>
44> M = jiffy:decode(L,[return_maps]).
#{<<"message">> =>
#{<<"@id">> => <<"1V4jk-18041">>,<<"@to">> => <<"UserA">>,
<<"@type">> => <<"chat">>,<<"@xml:lang">> => <<"en">>,
<<"body">> =>
#{<<"#text">> =>
<<208,159,209,128,208,184,208,178,208,181,209,130,33,32,
92,110,32,208,154,208,176,208,...>>,
<<"@xml:lang">> => <<"en_US">>},
<<"properties">> =>
#{<<"@xmlns">> =>
<<"http://www.jivesoftware.com/xmlns/xmpp/properties">>,
<<"property">> =>
#{<<"name">> => <<"prevMessageId">>,
<<"value">> =>
#{<<"#text">> => <<"mB6D0-33">>,
<<"@type">> => <<"string">>}}}}}
I'm wondering why jiffy:decode
dropped second property called chatVersion
in the map M
.
If call jiffy:decode
without any option - it's ok. But result is not erlang map.
45> N = jiffy:decode(L).
{[{<<"message">>,
{[{<<"@xml:lang">>,<<"en">>},
{<<"@to">>,<<"UserA">>},
{<<"@id">>,<<"1V4jk-18041">>},
{<<"@type">>,<<"chat">>},
{<<"body">>,
{[{<<"@xml:lang">>,<<"en_US">>},
{<<"#text">>,
<<208,159,209,128,208,184,208,178,208,181,209,130,...>>}]}},
{<<"properties">>,
{[{<<"@xmlns">>,
<<"http://www.jivesoftware.com/xmlns/xmpp/propertie"...>>},
{<<"property">>,
{[{<<"name">>,<<"chatVersion">>},
{<<"value">>,
{[{<<"@type">>,<<"string">>},{<<"#text">>,<<"4">>}]}}]}},
{<<"property">>,
{[{<<"name">>,<<"prevMessageId">>},
{<<"value">>,
{[{<<"@type">>,<<"stri"...>>},
{<<"#tex"...>>,<<...>>}]}}]}}]}}]}}]}
@Beisenbek in JSON, keys are unique - you cannot have an object that has two "property"
keys. This part has to be modeled differently, e.g. "properties"
should hold an array.
thanks @kzemek! do you have any idea how to handle it? you mean modeling inside exml:parse or in other place?
@Beisenbek to losslessly translate XML element into a valid JSON object you could directly translate #xmlel{}
structures, which would result in a JSON object like this:
{"name": "...", "params": [{"name": "...", "value": "..."}], "children": [...]}
So basically:
to_json_obj(#xmlel{name = Name, params = Params, children = Children}) ->
[{"name", Name},
{"params", Params},
{"children", [to_json_obj(Child) || Child <- Children]]}].
@Beisenbek Since #1976 is merged, can we close this issue?
@fenek yep, thanks!
MongooseIM version: 3.1.0 Installed from: source Erlang/OTP version: latest with source
Hello,
I have problem with out of band messages and REST API. When I'm trying send oob message via REST API :
{ "body": "some text", "x": { "xmlns": "jabber:x:oob", "url": "http://XXX.XXX.XXX.XXX/jabber/782ac6570eb7e0f5748ab66d8824d831bcfc8159ffbd0fea4ec13dacb0bc4b56/5F93DCE3-258D-4119-B3A8-9A886D422A85.jpeg" } }
in client xmpp side I receive only /body without oob teg <x xmlns="jabber:x:oob"><url>http://XXX.XXX.XXX.XXX/jabber/782ac6570eb7e0f5748ab66d8824d831bcfc8159ffbd0fea4ec13dacb0bc4b56/5F93DCE3-258D-4119-B3A8-9A886D422A85.jpeg</url></x>
Hi @Denismih
Can you please create a new thread for your issue? :)
MongooseIM version: 3.0.0 Installed from: source Erlang/OTP version: 18
I found that REST API for
One-to-one messages
returns partial info about messages. There are no extension part of messages. Only body. Request:And there are no archive_chat_markers in result set. But it enabled in config: