Problem with decoding backslash in JSON

I’m faced with a problem in websockets when I received a message with backslash. I escapes backslash with double ‘’ but error is raised.

So I decide to check how Poison lib is working in iex console:
Poison.decode! "{\"string\":\"a\\b\"}"
return me:
%{"string" => "a\b"}
and
Poison.decode! "{\"string\":\"a\\c\"}"
return me:
** (Poison.SyntaxError) Unexpected token: c
but I expect
%{"string" => "a\c"}

What wrong with my string?

Your iex example is not correct. You have to escape a little bit more. Your test wit some more steps look like this.

iex(10)> input = "{\"string\":\"a\\b\"}"
"{\"string\":\"a\\b\"}"
iex(11)> IO.puts(input)
{"string":"a\b"}
:ok
iex(12)> result = Poison.decode!(input) 
%{"string" => "a\b"}
iex(13)> IO.puts(Map.get(result, "string"))
a^H
:ok

I think that is not what you want.
You have to escape like this.

iex(14)> input = "{\"string\":\"a\\\\b\"}"
"{\"string\":\"a\\\\b\"}"
iex(15)> IO.puts(input)
{"string":"a\\b"}
:ok
iex(16)> result = Poison.decode!(input)
%{"string" => "a\\b"}
iex(17)> IO.puts(Map.get(result, "string"))
a\b
:ok 

and then

Poison.decode! "{\"string\":\"a\\\\c\"}"

will also work.

But

will return me result with double backslashes, but I need only one…
%{"string" => "a\\c"}

No, that is the representation of the string with the escaped backslash. If you print the string with IO.puts you get a\b.

Try the sigli ~S in iex.

iex(21)> ~S|{"string":"a\\c"}|                # generates a string with no escaping
"{\"string\":\"a\\\\c\"}"                     # the string with escaping
iex(22)> Poison.decode(~S|{"string":"a\\c"}|) # escaped just for the parser
{:ok, %{"string" => "a\\c"}}
iex(23)> ~S|a\c|
"a\\c"
iex(24)> IO.puts("a\\c")
a\c
:ok
5 Likes

you have two levels of escaping going on. one for iex and one for json

“{“string”:“a\\b”}” in iex is equivalent to the json string ‘{“string”:“a\b”}’ which is the literal character “a” followed by the escape sequence for a backspace

if you want the literal character “a” followed by the literal character “” followed by the literal character “b” you want the string ‘{“string”:“a\\b”}’ which written in iex/elixir is “{“string”:“a\\\\b”}”

2 Likes

I was confused when this worked, even though I had already tried using the ~s sigil without success.

It turns out that the ~S sigil (with a capital ‘S’) doesn’t do any interpolation and ignores escape codes, which solved the problem for me:

https://hexdocs.pm/elixir/sigils.html#interpolation-and-escaping-in-string-sigils

1 Like