Last active
April 3, 2018 14:57
-
-
Save vysakh0/e6aacc9c87e006144d96 to your computer and use it in GitHub Desktop.
Parse xml to elixir maps including the attributes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Parser do | |
require Record | |
Record.defrecord :xmlElement, Record.extract(:xmlElement, from_lib: "xmerl/include/xmerl.hrl") | |
Record.defrecord :xmlText, Record.extract(:xmlText, from_lib: "xmerl/include/xmerl.hrl") | |
Record.defrecord :xmlAttribute, Record.extract(:xmlAttribute, from_lib: "xmerl/include/xmerl.hrl") | |
def run(xml) do | |
run(xml, '//response') | |
end | |
def run(xml, path) do | |
{doc, _} = xml |> :binary.bin_to_list |> :xmerl_scan.string | |
:xmerl_xpath.string(path, doc) | |
|> parse(%{}, []) | |
end | |
defp parse([], result, _), do: result | |
defp parse([node | tail], result, path) when Record.is_record(node, :xmlElement) do | |
content = xmlElement(node, :content) | |
name = xmlElement(node, :name) | |
attributes = xmlElement(node, :attributes) | |
new_path = path ++ [name] | |
result = put_result(result, content, "", new_path) | |
result = parse(content, result, new_path) | |
result = parse(attributes, result, new_path) | |
parse(tail, result, path) | |
end | |
defp parse([node | tail], result, path) when Record.is_record(node, :xmlAttribute)do | |
name = xmlAttribute(node, :name) | |
val = xmlAttribute(node, :value) |> to_string | |
parent_path = List.delete_at(path, -1) | |
if parent_path !== [], do: children = get_in(result, parent_path) | |
if is_list(children) do | |
{[new], new_children}= Enum.partition(children, fn(x) -> Map.has_key?(x, List.last(path)) end) | |
value = new_children ++ [Map.put(new, name, val)] | |
result = put_in(result, parent_path, value) | |
else | |
new_path = path ++ [name] | |
result = put_in(result, new_path, val) | |
end | |
parse(tail, result, path) | |
end | |
defp parse([content | tail], result, path) when Record.is_record(content, :xmlText)do | |
{name, _} = xmlText(content, :parents) |> hd | |
value = xmlText(content, :value) |> to_string | |
parent_path = List.delete_at(path, -1) | |
children = get_in(result, parent_path) | |
if is_list(children) do | |
new = Map.put(%{}, name, value) | |
value = children ++ [new] | |
result = put_in(result, parent_path, value) | |
else | |
result = put_in(result, path, value) | |
end | |
parse(tail, result, path) | |
end | |
defp put_result(result, contents, _attrs, path) when length(contents) > 1 do | |
put_in(result, path, []) | |
end | |
defp put_result(result, [content], _attr, path) when Record.is_record(content, :xmlElement) do | |
put_in(result, path, %{}) | |
end | |
defp put_result(result, _, _, _), do: result | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ParserTest do | |
use ExUnit.Case | |
@xml """ | |
<result> | |
<event id="2"> | |
<title>My event</title> | |
<artist id="4"> | |
<name>Michael Jackson</name> | |
</artist> | |
<artist id="5"> | |
<name>Jackson</name> | |
</artist> | |
</event> | |
<event id="3"> | |
<title>My event 2</title> | |
<artist id="1"> | |
<name>Rolling Stones</name> | |
</artist> | |
</event> | |
<abcd><title>sdf</title></abcd> | |
<lo id="23"><title>sdf</title></lo> | |
<xyz><title>sdf</title><desc>hi</desc></xyz> | |
<lmn><title>sdf</title><desc value="hi">hi</desc></lmn> | |
</result> | |
""" | |
test "xml parser " do | |
assert Parser.run(@xml, '//abcd') == %{abcd: %{title: "sdf"}} | |
assert Parser.run(@xml, '//xyz') == %{xyz: [%{title: "sdf"}, %{desc: "hi"}]} | |
assert Parser.run(@xml, '//lo') == %{lo: %{title: "sdf", id: "23"}} | |
assert Parser.run(@xml, '//lmn') == %{lmn: [%{title: "sdf"}, %{desc: "hi", value: "hi"}]} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment