defmodule Retroper do @moduledoc """ `Retroper` is the tool to convert between `Estructura.Tree` instances, _XML_ and _JSON_. To make it isomorphic, _JSON_ is supposed to be presented in the form of `Estructura.Tree`-like nested objects, all having `name`, `attributes`, and `content` fields. """ alias XmlToMap, as: XmlMap {:ok, model} = :erlsom.compile_xsd_file(~s"specs/dealconfirmation_42.xsd") @model model def json_to_xml(json, model \\ @model) when is_binary(json) do with {:ok, map} <- Jason.decode(json), do: map_to_xml(map, model) end def xml_to_xml(xml, model \\ @model) when is_binary(xml) do with %{} = map <- XmlMap.nested_map(xml), do: map_to_xml(map, model) end def xml_to_json(xml, model \\ @model) when is_binary(xml) do with %{} = map <- XmlMap.nested_map(xml), do: map_to_json(map, model) end def json_to_json(json, model \\ @model) when is_binary(json) do with {:ok, %{} = map} <- Jason.decode(json), do: map_to_json(map, model) end def map_to_json(map, model \\ @model) do with {:ok, tree} <- Estructura.Tree.coerce(map), ast = Estructura.Tree.to_ast(tree), xml = XmlBuilder.generate([:xml_decl, ast], standalone: true), {:ok, _, _} <- :erlsom.scan(xml, model), do: Jason.encode(tree) end def map_to_xml(map, model \\ @model) do with {:ok, tree} <- Estructura.Tree.coerce(map), ast = Estructura.Tree.to_ast(tree), xml = XmlBuilder.generate([:xml_decl, ast], standalone: true), {:ok, _, _} <- :erlsom.scan(xml, model), do: {:ok, xml} end for from <- ~w|json map xml|a, to <- ~w|json xml| do def unquote(:"#{from}_to_#{to}!")(map, model \\ @model) do case unquote(:"#{from}_to_#{to}")(map, model) do {:ok, result} -> result {:error, reason} -> raise ArgumentError, message: "Cannot convert `#{unquote(from)}` to `#{unquote(to)}, reason: #{inspect(reason)}" end end end end