defmodule MapToXml do @moduledoc """ Documentation for `MapToXml`. ## Installation ```elixir def deps do [ {:elixir_map_to_xml, "~> 0.1.0"} ] end ``` """ import XmlBuilder @doc """ Convert a map to an XML document. ## Examples ### Basic example ```elixir MapToXml.from_map(%{ "tag1" => "value1", "tag2" => "value2", "tag3" => "value3" }) ``` will output: ```xml value1 value2 value3 ``` ### Nested maps ```elixir MapToXml.from_map(%{ "tag1" => %{ "tag2" => %{ "tag3" => "value" } } }) ``` will output: ```xml value ``` ### Repeated child tags ```elixir MapToXml.from_map(%{ "Tags" => %{ "Tag1" => [ %{"Sub1" => "Val1"}, %{"Sub1" => "Val2"}, %{"Sub1" => "Val3"} ] } }) ``` will output: ```xml Val1 Val2 Val3 ``` ### Attributes ```elixir MapToXml.from_map(%{ "Tag1" => %{ "#content" => "some value", "-id" => 123, "-something" => "111" } }) ``` will output: ```xml some value ``` """ @spec from_map(map) :: binary def from_map(%{} = map) do map |> Map.keys() |> Enum.map(fn key -> build_tag(key, map[key]) end) |> document() |> generate() end defp build_tag(key, value, attributes \\ %{}) defp build_tag(key, %{} = map, attributes) do keys = Map.keys(map) if Enum.member?(keys, "#content") do attributes = for key <- keys, String.slice(key, 0, 1) == "-", into: %{} do {String.slice(key, 1..-1), map[key]} end build_tag(key, map["#content"], attributes) else tags = for key <- keys do build_tag(key, map[key]) end element(key, attributes, tags) end end defp build_tag(key, [_ | _] = values, attributes) do for value <- values, do: build_tag(key, value, attributes) end defp build_tag(key, value, attributes) do element(key, attributes, "#{value}") end end