%%% @copyright (C) 2017, AdRoll %%% @doc %%% %%% Helper for parsing XML returned by certain AWS APIs. %%% %%% @end %%% Created : 2 Jun 2017 by Mike Watters -module(erliam_xml). -export([parse/1]). -include_lib("xmerl/include/xmerl.hrl"). %%%% API parse(Xml) -> {E, _} = xmerl_scan:string(unicode:characters_to_list(Xml, utf8)), parse_element(E). %%%% INTERNAL FUNCTIONS parse_element(#xmlElement{name = Name, content = Content}) -> {Name, case Content of [#xmlElement{} | _] -> [{ChildName, parse_element(ChildContent)} || #xmlElement{name = ChildName, content = ChildContent} <- Content]; _ -> parse_element(Content) end}; parse_element([#xmlText{} | _] = L) -> case lists:all(fun (E) -> is_record(E, xmlText) end, L) of true -> unescape_xml_text(xmerl_xs:value_of(L)); false -> %% if a list of elements is not solely text, discard the text elements and parse %% the non-text elements: parse_element([X || X <- L, not is_record(X, xmlText)]) end; parse_element(L) when is_list(L) -> [parse_element(X) || X <- L]; parse_element(X) -> X. unescape_xml_text(X) -> unescape_xml_text(unicode:characters_to_binary(X, utf8), [{<<"<">>, <<"<">>}, {<<">">>, <<">">>}, {<<""">>, <<"\"">>}, {<<"'">>, <<"'">>}, {<<"&">>, <<"&">>}]). unescape_xml_text(X, []) -> X; unescape_xml_text(X, [{C, R} | T]) -> unescape_xml_text(binary:replace(X, C, R, [global]), T). %%%% TESTS -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). basic_decode_test() -> Data = <<"\n \n \n " " ACCESS_KEY_ID\n SECR" "ET_ACCESS_KEY\n SESSION_TOKEN\n EXPIRATION\n \n \n \n REQUEST_ID\n \n \n">>, ?assertEqual({'GetSessionTokenResponse', [{'GetSessionTokenResult', [{'Credentials', [{'AccessKeyId', <<"ACCESS_KEY_ID">>}, {'SecretAccessKey', <<"SECRET_ACCESS_KEY">>}, {'SessionToken', <<"SESSION_TOKEN">>}, {'Expiration', <<"EXPIRATION">>}]}]}, {'ResponseMetadata', [{'RequestId', <<"REQUEST_ID">>}]}]}, parse(Data)). -endif.