Skip to content

Instantly share code, notes, and snippets.

@remi6397
Created March 21, 2018 12:04
Show Gist options
  • Save remi6397/836730facb9c4206cdb507a136909dbf to your computer and use it in GitHub Desktop.
Save remi6397/836730facb9c4206cdb507a136909dbf to your computer and use it in GitHub Desktop.
Several parts of XML <-> JSON converter (taken from my Phoenix webapp)
defmodule PastebinderWeb.JSONToXmlHelper do
def to_xml(map) do
list = to_list(map)
:xmerl.export_simple(list, :xmerl_xml)
|> List.flatten
|> to_string
end
defp to_list(map) when is_map(map) do
keyword = Map.to_list(map)
Enum.map(keyword, fn({k, v}) -> {k, [], to_list(v)} end)
end
defp to_list(list) when is_list(list), do: Enum.map(list, fn(x) -> {:item, [], to_list(x)} end)
defp to_list(some), do: [to_charlist some]
end
defmodule PastebinderWeb.MapToKeywordHelper do
def convert(map) when is_map(map) do
keyword = Map.to_list(map)
Enum.map keyword, fn({k, v}) -> {k, convert(v)} end
end
def convert(list) when is_list(list), do: Enum.map(list, &convert/1)
def convert(some), do: some
end
defmodule PastebinderWeb.PasteView do
use PastebinderWeb, :view
alias PastebinderWeb.PasteView
def render("index.json", %{pastes: pastes}) do
%{data: render_many(pastes, PasteView, "paste.json")}
end
def render("show.json", %{paste: paste}) do
%{data: render_one(paste, PasteView, "paste.json")}
end
def render("paste.json", %{paste: paste}) do
%{id: paste.id,
title: paste.title,
content: paste.content,
visibility: paste.visibility,
author: render_one(Pastebinder.Accounts.get_user!(paste.author_id), PastebinderWeb.UserView, "user.json")}
end
def render(resource, opts) do
if String.ends_with?(resource, ".xml") do
render(List.last(Regex.run(~r/^(.*)\.xml$/, resource)) <> ".json", opts)
|> PastebinderWeb.JSONToXmlHelper.to_xml
else
raise FunctionClauseError
end
end
end
defmodule Plug.Parsers.XML do
@behaviour Plug.Parsers
import Plug.Conn
def init(opts) do
opts
end
def parse(conn, _, "xml", _headers, opts) do
decoder = Keyword.get(opts, :xml_decoder) || raise ArgumentError, "XML parser expects a :xml_decoder option"
conn
|> read_body(opts)
|> decode(decoder)
end
def parse(conn, _type, _subtype, _headers, _opts) do
{:next, conn}
end
defp decode({:ok, body, conn}, decoder) do
case decoder.string(String.to_charlist(body)) do
{parsed, []} ->
{:ok, to_map([to_json(parsed)]), conn}
_error ->
raise "Malformed XML"
end
end
defp to_map(keyword) do
case keyword do
[{k, v}] ->
%{to_string(k) => to_map(v)}
[{k, v} | tl] ->
Map.put to_map(tl), to_string(k), to_map(v)
list when is_list(list) ->
Enum.map list, &to_map/1
el -> el
end
end
defp to_json([{:xmlElement, :item, :item, _, _ns, _, _, _attrs, contents, _, _, _}]) do
[to_json(contents)]
end
defp to_json([{:xmlElement, :item, :item, _, _ns, _, _, _attrs, contents, _, _, _} | tl]) do
[to_json(contents) | to_json(tl)]
end
defp to_json([{:xmlElement, tag, tag, _, _ns, _, _, _attrs, contents, _, _, _}]) do
[{tag, to_json(contents)}]
end
defp to_json([{:xmlElement, tag, tag, _, _ns, _, _, _attrs, contents, _, _, _} | tl]) do
[{tag, to_json(contents)} | to_json(tl)]
end
defp to_json([{:xmlText, _, _, _, text, :text}]) do
to_json(text)
end
defp to_json({:xmlElement, tag, tag, _, _ns, _, _, _attrs, contents, _, _, _}) do
{tag, to_json(contents)}
end
defp to_json('nil'), do: nil
defp to_json('true'), do: true
defp to_json('false'), do: false
defp to_json(t) do
IO.write "-- Parsing --\n"
IO.inspect t
try do
try do
String.to_integer(to_string(t))
rescue
_ -> String.to_float(to_string(t))
end
rescue
_ -> to_string t
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment