sweet_xml
sweet_xml copied to clipboard
Looping via xmap
<Query>
<cars>
<bmw>
<specs>
<model>5 seriesw</model>
<engine>4.4</engine>
</specs>
</bmw>
<bmw>
<specs>
<model>3 seriesw</model>
<engine>3.0</engine>
</specs>
</bmw>
</cars>
</Query>
Im parsing this via SweetXml
request_body |> parse |> xmap(
cars: [
~x"//Query/cars"k,
bmw: [
~x"./bmw"kl,
specs: [
~x"./specs"k,
model: ~x"./model/text()"s,
engine: ~x"./engin/text()"s
]
]
]
)
I want to get data like this format:
[
Query: [
cars: [
bmw: [
specs: [
model: "5series"
engine: 4,4
]
],
bmw: [
specs: [
model: "5series"
engine: 4,4
]
]
]
]
]
Problem is that I get
[cars: [bmw: [specs: [[model: "5 seriesw", engin: ""]]]]]
Its not looping on Query/cars/*
Using xpath instead of xmap, and "//Query" instead of "//Query/cars" comes close to the desired output:
request_body |> xpath(
~x"//Query"k,
cars: [
~x"./cars"k,
bmw: [
~x"./bmw"lk,
specs: [
~x"./specs"k,
model: ~x"./model/text()"s,
engine: ~x"./engin/text()"s
]
]
]
)
Output:
[cars:
[bmw:
[
[specs: [model: "5 seriesw", engine: ""]],
[specs: [model: "3 seriesw", engine: ""]]
]
]
]
@jamesnolanverran thanks I see its close to this but I need this ouput somehow
[cars:
[bmw:
[
[specs: [model: "5 seriesw", engine: ""]]
],
[bmw:
[
[specs: [model: "3 seriesw", engine: ""]]
]
]
]
logically this code should generate, but it does not
request_body |> xpath(
~x"//Query"k,
cars: [
~x"./cars"lk,
bmw: [
~x"./bmw"k,
specs: [
~x"./specs"k,
model: ~x"./model/text()"s,
engine: ~x"./engin/text()"s
]
]
]
)
Actually we have experienced the same issue while addressing lists using the format ~x"//some/path"l
.
I've checked code in the library but it seems quite a tricky part. Any chance to be fixed?
Thanks!
*Edited - here's one I came up with. A little long winded though... *Edited again to get rid of duplicated code
def run do
base_node = request_body |> xpath(~x"//Query"l)
paths = ["cars", "bmw", "specs"]
process_node(base_node, paths)
end
def process_node(outer_node, [ path | tail ]) do
outer_node
|> Enum.map(fn (node) ->
inner_node = node |> xpath(~x"./#{path}"l)
{
node |> xpath(~x"name(.)"s) |> String.to_atom,
process_node(inner_node, tail)
}
end)
end
def process_node(outer_node, []) do
outer_node
|> Enum.map(fn (node) ->
{
node |> xpath(~x"name(.)"s) |> String.to_atom,
outer_node |> spec_details |> List.flatten
}
end)
end
def spec_details(node) do
node
|> Enum.map(fn (spec_node) ->
[
model: spec_node |> xpath(~x"./model/text()"s),
engine: spec_node |> xpath(~x"./engine/text()"s)
]
end)
end
# OUTPUT:
[Query: [cars: [bmw: [specs: [model: "5 seriesw", engine: "4.4"]],
bmw: [specs: [model: "3 seriesw", engine: "3.0"]]]]]
Still playing with this a bit - I wrote a test that fails.
test "read me examples", %{simple: simple, readme: readme} do
# ....
result = readme |> xmap(
matchups: [
~x"//game/matchups/matchup"lk,
teams: [
~x"./teams"lk,
team: [
~x"./team"lk,
name: ~x"./name/text()"s,
id: ~x"./id/text()"s
]
]
]
)
assert result == %{
matchups: [
[teams: [
[team: [name: "Team One", id: "1"]],
[team: [name: "Team Two", id: "2"]]
]
],
[teams: [
[team: [name: "Team Two", id: "2"]],
[team: [name: "Team Three", id: "3"]]
]
],
[teams: [
[team: [name: "Team One", id: "1"]],
[team: [name: "Team Three", id: "3"]]
]
]
]
}