Packages

Parsing support for Apple's property list formats

Current section

Files

Jump to
pealist lib xml.ex
Raw

lib/xml.ex

defmodule Pealist.XML do
require Record
@moduledoc false
Record.defrecordp(
:element_node,
:xmlElement,
Record.extract(:xmlElement, from_lib: "xmerl/include/xmerl.hrl")
)
Record.defrecordp(
:text_node,
:xmlText,
Record.extract(:xmlText, from_lib: "xmerl/include/xmerl.hrl")
)
def decode(xml) do
{doc, _} =
xml
|> :binary.bin_to_list()
|> :xmerl_scan.string([{:comments, false}, {:space, :normalize}])
root =
doc
|> element_node(:content)
|> Enum.reject(&empty?/1)
|> Enum.at(0)
parse_value(root)
end
defp parse_value(element_node() = element) do
parse_value(element_node(element, :name), element_node(element, :content))
end
defp parse_value(:string, list) do
do_parse_text_nodes(list, "")
end
defp parse_value(:date, nodes) do
parse_value(:string, nodes)
end
defp parse_value(:data, nodes) do
{:ok, data} =
parse_value(:string, nodes)
|> Base.decode64(ignore: :whitespace)
data
end
defp parse_value(true, []), do: true
defp parse_value(false, []), do: false
defp parse_value(:integer, nodes) do
parse_value(:string, nodes) |> String.to_integer()
end
defp parse_value(:real, nodes) do
{value, ""} = parse_value(:string, nodes) |> Float.parse()
value
end
defp parse_value(:array, contents) do
contents
|> Enum.reject(&empty?/1)
|> Enum.map(&parse_value/1)
end
defp parse_value(:dict, contents) do
{keys, values} =
contents
|> Enum.reject(&empty?/1)
|> Enum.split_with(fn element ->
element_node(element, :name) == :key
end)
unless length(keys) == length(values), do: raise("Key/value pair mismatch")
keys
|> Enum.map(&element_text_value/1)
|> Enum.zip(values)
|> Enum.into(%{}, fn {key, element} ->
{key, parse_value(element)}
end)
end
defp element_text_value(element) do
case element_node(element, :content) do
[content_node] ->
content_node
|> text_node(:value)
|> :unicode.characters_to_binary()
[] ->
""
end
end
defp do_parse_text_nodes([], result), do: result
defp do_parse_text_nodes([node | list], result) do
text = node |> text_node(:value) |> :unicode.characters_to_binary()
do_parse_text_nodes(list, result <> text)
end
defp empty?({:xmlText, _, _, [], ~c" ", :text}), do: true
defp empty?(_), do: false
end