Current section

Files

Jump to
elixir_xml_to_map lib xml_to_map nested_map.ex
Raw

lib/xml_to_map/nested_map.ex

defmodule XmlToMap.NestedMap do
@moduledoc """
Module to recursively traverse the output of `erlsom.simple_form/1`
and produce a nested map with `name`, `attributes`, and `content` fields.
`erlsom` uses character lists so this library converts them to
Elixir binary string.
"""
@moduledoc since: "3.1.0"
@typedoc "Type of a nested map"
@type element :: %{
name: String.t(),
attributes: map() | [{charlist() | String.t(), charlist() | String.t()}],
content: [element()]
}
# if a single list element of a tuple, ignore the list detail and parse it
def parse([{tag, attributes, content}], purge_empty) do
parse({tag, attributes, content}, purge_empty)
end
# for any other singular value, it's probably meant to be a string
def parse([value], _purge_empty) do
value |> to_string() |> String.trim()
end
# a parse with a tuples is probably the entry point we'll hit first
# if this node has no attributes, our work is easier
def parse({tag, [], []}, true) do
%{name: to_string(tag)}
end
def parse({tag, [], content}, true) do
%{name: to_string(tag), content: parse(content, true)}
end
def parse({tag, attributes, []}, true) do
%{name: to_string(tag), attributes: fix_attributes(attributes)}
end
def parse({tag, attributes, content}, purge_empty) do
%{
name: to_string(tag),
attributes: fix_attributes(attributes),
content: parse(content, purge_empty)
}
end
def parse([], _purge_empty), do: nil
def parse(list, purge_empty) when is_list(list), do: Enum.map(list, &parse(&1, purge_empty))
defp fix_attributes(attributes) do
for {k, v} <- attributes do
k = if is_list(k) and List.ascii_printable?(k), do: to_string(k), else: k
v = if is_list(v) and List.ascii_printable?(v), do: to_string(v), else: v
{k, v}
end
end
end