Awesome
SAXMap
Converts an XML String or an XML file stream to a Map.
Benefit from Saxy's SAX mode, this library has a good conversion efficiency.
Installation
def deps do
[
{:sax_map, "~> 1.2"}
]
end
Example
iex(1)> xml = """
...(1)> <?xml version="1.0" encoding="UTF-8"?>
...(1)> <thread>
...(1)> <title>Hello</title>
...(1)> <items>
...(1)> <item>item1</item>
...(1)> <item>item2</item>
...(1)> </items>
...(1)> </thread>
...(1)> """
iex(2)> SAXMap.from_string(xml)
{:ok,
%{
"thread" => %{"items" => %{"item" => ["item1", "item2"]}, "title" => "Hello"}
}}
By default SAXMap.from_string
will ignore all attributes of elements in the result, if you want to merge the attributes as the child elements, please use ignore_attribute
option to achieve this:
xml = """
<thread version="1">
<title color="red" font="16">Hello</title>
<items size="3">
<item font="12">item1</item>
<item font="12">item2</item>
<item font="12">item3</item>
</items>
</thread>
"""
SAXMap.from_string(xml, ignore_attribute: false)
{:ok,
%{
"thread" => %{
"content" => %{
"items" => %{
"content" => %{
"item" => [
%{"content" => "item1", "font" => "12"},
%{"content" => "item2", "font" => "12"},
%{"content" => "item3", "font" => "12"}
]
},
"size" => "3"
},
"title" => %{"color" => "red", "content" => "Hello", "font" => "16"}
},
"version" => "1"
}
}}
SAXMap.from_string(xml, ignore_attribute: {false, "@"})
{:ok,
%{
"thread" => %{
"@version" => "1",
"content" => %{
"items" => %{
"@size" => "3",
"content" => %{
"item" => [
%{"@font" => "12", "content" => "item1"},
%{"@font" => "12", "content" => "item2"},
%{"@font" => "12", "content" => "item3"}
]
}
},
"title" => %{"@color" => "red", "@font" => "16", "content" => "Hello"}
}
}
}}
Notice: The ignore_attribute: false
equals ignore_attribute: {false, ""}
, in this case, the child elements will be automatically naming with "content" as the key of the key-value pair to distinct this key-value pair is from content or attribute.
Benchmark
Only for your reference, all credit goes to Saxy, the details of benchmark can be found in the bench
directory of the repository.
Run:
mix run xml_to_map.exs
Output:
Operating System: macOS
CPU Information: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Number of Available Cores: 16
Available memory: 32 GB
Elixir 1.12.2
Erlang 24.0.4
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 10 s
memory time: 2 s
parallel: 1
inputs: none specified
Estimated total run time: 42 s
Benchmarking SAXMap.from_string ignore attribute...
Benchmarking SAXMap.from_string with attribute...
Benchmarking XmlToMap.naive_map...
Name ips average deviation median 99th %
SAXMap.from_string ignore attribute 105.03 K 9.52 μs ±129.42% 9 μs 33 μs
SAXMap.from_string with attribute 96.74 K 10.34 μs ±110.08% 9 μs 35 μs
XmlToMap.naive_map 26.31 K 38.01 μs ±46.21% 33 μs 105 μs
Comparison:
SAXMap.from_string ignore attribute 105.03 K
SAXMap.from_string with attribute 96.74 K - 1.09x slower +0.82 μs
XmlToMap.naive_map 26.31 K - 3.99x slower +28.49 μs
Memory usage statistics:
Name Memory usage
SAXMap.from_string ignore attribute 14.61 KB
SAXMap.from_string with attribute 16.69 KB - 1.14x memory usage +2.08 KB
XmlToMap.naive_map 40.90 KB - 2.80x memory usage +26.29 KB
**All measurements for memory usage were the same**