sweet_xml icon indicating copy to clipboard operation
sweet_xml copied to clipboard

Looping via xmap

Open programisti opened this issue 8 years ago • 5 comments

<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/*

programisti avatar Jun 02 '16 13:06 programisti

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 avatar Jun 03 '16 01:06 jamesnolanverran

@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
          ]
        ]
      ]
    )

programisti avatar Jun 03 '16 08:06 programisti

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!

xurde avatar Jun 03 '16 11:06 xurde

*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"]]]]]

jamesnolanverran avatar Jun 03 '16 19:06 jamesnolanverran

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"]]
        ]
      ]
    ]
  }

jamesnolanverran avatar Jun 09 '16 20:06 jamesnolanverran