michalmuskala / jason

A blazing fast JSON parser and generator in pure Elixir.
Other
1.6k stars 170 forks source link

JSON decode problem with escaped double-quotes (bug?) #113

Closed sofakingworld closed 4 years ago

sofakingworld commented 4 years ago

Jason version: 1.2.1

JSON

{"name": "A \"B\""}

Jason.decode result:

iex(17)> json = """
...(17)> {"name": "A \"B\""}
...(17)> """
"{\"name\": \"A \"B\"\"}\n"

iex(18)> Jason.decode json
{:error,
 %Jason.DecodeError{data: "{\"name\": \"A \"B\"\"}\n", position: 13, token: nil}}

Ruby JSON result

irb(main):039:0> require 'JSON'
irb(main):040:0> JSON.parse '{"name": "A \"B\""}'
=> {"name"=>"A \"B\""}

Chrome console image

michalmuskala commented 4 years ago

This is about escapes - there's a difference in behaviour in Elixir """ and Ruby's '.

iex(3)> json = """
...(3)> {"name": "A \"B\""}
...(3)> """
"{\"name\": \"A \"B\"\"}\n"

vs

irb(main):001:0> '{"name": "A \"B\""}'
=> "{\"name\": \"A \\\"B\\\"\"}"

To get the same value in Elixir the easiest is to use ~S

iex(1)> json = ~S|{"name": "A \"B\""}|
"{\"name\": \"A \\\"B\\\"\"}"
iex(2)> Jason.decode(json)
{:ok, %{"name" => "A \"B\""}}
sofakingworld commented 4 years ago

By the way, found some weird behavior:

json = ~S|{"name": "A \"B\""}|

Jason.decode(inspect(json))
# => {:ok, "{\"name\": \"A \\\"B\\\"\"}"}
michalmuskala commented 4 years ago

This behavior is entirely correct. Inspect accidentally turns a string containing JSON into a single string representation of that that also happens to be a valid JSON. Decoding reverses that.

This would be the same as:

> JSON.decode!(inspect("a"))
"a"

It won't always happen, but in limited cases such as this where the only conversion is escaping quotes JSON.decode! happens to be the reverse of inspect.