4

I have parsed the following JSON using Posion.decode!

json = %{"color-Black|size:10" => 
    %{"attributes" => %{"color" => "Black","size" => "11"},    
      "isAvailable" => true,      
    "pricing" => %{"standard" => "$415.00", "sale" => 415}}, 
 "color|size-EU:9.5" => 
    %{"attributes" => %{"color" => "Black","size" => "11"},    
      "isAvailable" => true,      
    "pricing" => %{"standard" => "$415.00", "sale" => 415}}}

I want to map this and I'm unable to get JSON elements as the text in the node element changes. So far I've tried.

Enum.map(json , fn(item) ->
%{
  color: item.attributes["color"],                 
  size: item.attributes["size"],
  price: item.pricing["standard"] * 100,
  isAvailable: item.isAvailable
 }
end)

But this code gives some error related to accessing.

3 Answers 3

5

While mapping the map, the iterated key-value pairs come to the mapper as tuples {key, value}:

Enum.map(json, fn {_, %{"attributes" => attributes,
                        "isAvailable" => isAvailable,
                        "pricing" => pricing}} ->
  %{
    color: attributes["color"],
    size: attributes["size"],
    price: pricing["standard"],
    isAvailable: isAvailable
   }
end)

#⇒ [
#    %{color: "Black", isAvailable: true, price: "$415.00", size: "11"},
#    %{color: "Black", isAvailable: true, price: "$415.00", size: "11"}
# ]

Here we use an inplace pattern matching for values in mapper to simplify the code of matcher itself and make it less error-prone in a case of bad input.

Sign up to request clarification or add additional context in comments.

Comments

2

Three things:

  1. You have a map so Enum.map will give you a tuple of key and value. You just want the value here, so do:

    fn {_, item} ->
    
  2. The keys in your map are strings. The dot syntax only works for atom keys. You need to do:

    item["attributes"]
    

    instead of

    item.attributes
    

    and similar for other keys.

  3. The price you have is a string. You'll need to convert it to a Float before you can multiply it. You can do it like this using String.trim_leading and String.to_float:

    iex(1)> "$123.45" |> String.trim_leading("$") |> String.to_float
    123.45
    

Enum.map(json, fn {_, item} ->
  %{
    color: item["attributes"]["color"],
    size: item["attributes"]["size"],
    price: item["pricing"]["standard"] |> String.trim_leading("$") |> String.to_float |> Kernel.*(100),
    isAvailable: item["isAvailable"]
  }
end)
|> IO.inspect

Output:

[%{color: "Black", isAvailable: true, price: 4.15e4, size: "11"},
 %{color: "Black", isAvailable: true, price: 4.15e4, size: "11"}]

Comments

1

You're getting access errors because you can only use the thing.property syntax if property is an atom and in your json map, the keys are strings.

One thing that will make this much easier is that Poison.decode can take a second argument of keys: :atoms to return a map with atom keys. Working with what you have here, this will solve the problem:

json_atoms = Poison.encode!(json) |> Poison.decode!(keys: :atoms)
convert_price = fn x -> 
  String.trim_leading(x, "$")
  |> String.to_float
  |> &(&1 * 100)
  |> trunc
end

Enum.map(json_atoms, fn {_k,v} -> %{
  color: v.attributes.color,
  size: v.attributes.size,
  price: convert_price.(v.pricing.standard),
  isAvailable: v.isAvailable
} end)

2 Comments

Word of warning: atoms don't get garbage collected, so if you parse a lot of JSON with different keys, this could become a problem. github.com/devinus/poison#parser
There is an atom limit and they're not garbage collected (yet), but in this case, the JSON has a consistent shape. So the only atoms created would be :size, :color, :price and :is_available. I've never seen an app get anywhere near the limit FWIW.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.