0

I need to encode some hash containing URL string. I use to_json method and I need backslash in front of each slash (as PHP print such strings). For example:

hash = {"url":"http:\\/\\/example.com\\/test"}
hash.to_json

The result is

{:url=>"http:\\/\\/example.com\\/test"}

While I need (and PHP's json_encode returns string with a single backslash).

{:url=>"http:\/\/example.com\/test"}

It's very important to keep the string as in PHP in case of encoding. Because strings with double and single backslashes get different results.

UPD: The problem is not in communication. I need to encode my JSON using HMAC (SHA384). And the result is different in PHP and Ruby when I'm using URL strings. If the string doesn't contain backslash all works fine...

PHP implementation introduces the backslashes. JSON using by PHP looks so {"url":"http:\/\/example.com\/test"} while Ruby's JSON is {"url":"http:\\/\\/example.com\\/test"}

4
  • I know the difference between puts hash and p hash. But inside Ruby, as I think, it's anyway double backslash. Commented Jul 1, 2019 at 7:33
  • Your question is unclear. There are exactly three backslashes in your string, one before each slash. None of them is a double backslash. Also, the result that you claim comes from hash.to_json is actually a Ruby literal and not JSON. Commented Jul 1, 2019 at 8:23
  • @JörgWMittag {:url=>"http:\\/\\/example.com\\/test"} here is 2 backslashes before every slash, isn't it? I need a line where these 2 backslashes will be one backslash. Sorry for the misunderstanding, I asked as I see the problem. Commented Jul 1, 2019 at 8:45
  • No, there is only one backslash before every slash. In the output of String#inspect, backslashes are used as escape characters in the display to escape special characters. In this case, the first backslash that is displayed tells you that the backslash after it is actually part of the string and not an escape character. So, there are two backslashes displayed so that you know that there is only one backslash actually in the string. You can trivially test this by checking the length of the string. Commented Jul 13, 2019 at 13:34

4 Answers 4

3

My apologies, you do seem to have a valid issue on your hand. The key is this: Why is the slash an escapable character in JSON? and its duplicate target, JSON: why are forward slashes escaped?. Since both unescaped slashes and escaped slashes are allowed, Ruby chose to not escape them, and PHP chose to escape them, and both approaches are correct.

(Aside: there's a bit of a complication in talking about this because \ is an escape character both for a string literal, and for JSON strings. Thus, in this answer, I take care to puts (or echo/print_r) all the values, to see the strings that do not have the string literal backslash escapes, only the backslashes that are actually present in the strings.)

Thus, the JSON {"url":"http:\/\/example.com\/test"} is a representation of the Ruby hash { 'url' => 'http://example.com/test' }, where slashes are escaped (as PHP's json_encode would do it). Ruby's to_json' would render that as{"url":"http://example.com/test"}`:

# Ruby
json1 = '{"url":"http:\/\/example.com\/test"}'
puts json1                        # => {"url":"http:\/\/example.com\/test"}
puts JSON.parse(json1)            # => {"url"=>"http://example.com/test"}
puts JSON.parse(json1).to_json    # => {"url":"http://example.com/test"}

# PHP
$json1 = '{"url":"http:\/\/example.com\/test"}';
echo $json1;                           # {"url":"http:\/\/example.com\/test"}
print_r(json_decode($json1));          # stdClass Object
                                       # (
                                       #     [url] => http://example.com/test
                                       # )
echo json_encode(json_decode($json1)); # {"url":"http:\/\/example.com\/test"}

On the other hand, {"url":"http:\\/\\/example.com\\/test"} (represented in Ruby and PHP as the string '{"url":"http:\\\\/\\\\/example.com\\\\/test"}') is a representation of the Ruby hash { 'url' => 'http:\/\/example.com\/test' }, where there are actual backslashes, but the slashes are not escaped. PHP's json_encode would render this value as {"url":"http:\\\/\\\/example.com\\\/test"}.

# Ruby
json2 = '{"url":"http:\\\\/\\\\/example.com\\\\/test"}'
puts json2                        # => {"url":"http:\\/\\/example.com\\/test"}
puts JSON.parse(json2)            # => {"url"=>"http:\\/\\/example.com\\/test"}
puts JSON.parse(json2).to_json    # => {"url":"http:\\/\\/example.com\\/test"}

# PHP
$json2 = '{"url":"http:\\\\/\\\\/example.com\\\\/test"}';
echo $json2;                           # {"url":"http:\/\/example.com\/test"}
print_r(json_decode($json2));          # stdClass Object
                                       # (
                                       #     [url] => http:\/\/example.com\/test
                                       # )
echo json_encode(json_decode($json2)); # {"url":"http:\\\/\\\/example.com\\\/test"}

PHP json_encode has an option to prevent the PHP's default of escaping of backslashes:

# PHP
echo json_encode('/');                         # "\/"
echo json_encode('/', JSON_UNESCAPED_SLASHES); # "/"

Ruby does not have a similar option to force escaping of slashes, but since a slash has no special meaning in JSON, we can just manually replace / with \/:

# Ruby
puts '/'.to_json                  # "/"
puts '/'.to_json.gsub('/', '\/')  # "\/"
Sign up to request clarification or add additional context in comments.

1 Comment

The last line with gsub helps me! Thanks a lot!
1

Use single quotes around strings if you don't want to deal with escaping backslashes.

hash = { url: 'http:\/\/example.com\/test' }
json = hash.to_json
puts json

# => {"url":"http:\\/\\/example.com\\/test"}

Just a quick reminder: in JSON, backslashes need to be escaped because they are considered as control characters.

This way, when PHP parses this JSON document, you will get your string with a single backslash before each slash.

2 Comments

Yes, that's right about single quotes. But I need to keep them in my JSON result as a single backslash, while Ruby gives me only double backslash. Even with double quotes.
I mean { url: 'http:\/\/example.com\/test' } and { url: "http:\/\/example.com\/test" } got the same result {"url":"http:\\/\\/example.com\\/test"} while I need {"url":"http:\/\/example.com\/test"}
0

The problem behind your question is probably the real problem. I'm not sure because your question is not totally clear to me so I'm taking a guess/assumption here with my answer.

My assumption here is that you want to communicate between ruby and php, with json.

Well, in that case your don't have to have a problem (with backslashes).

Let ruby .to_json (JSON.generate(..)) and JSON.parse(..) solve the ruby part and let json_encode() and json_decode() solve the the php part and you are done.

so in ruby:
- don't use extra escaping-backslashes, let .to_json solve that for you
- use the literal url string you would type in your browser like so:

hash = {"url":"http://example.com/test"} # hash is a ruby object
puts hash.to_json                        # => {"url":"http://example.com/test"} is JSON (string)

then in php:

var_dump( json_decode('{"url": "http://example.com/test"}') );

gives you:

object(stdClass)#1 (1) {
  ["url"]=>
  string(23) "http://example.com/test"
}
var_dump( json_decode('{"url": "http:\/\/example.com\/test"}') );

gives you:

object(stdClass)#1 (1) {
  ["url"]=>
  string(23) "http://example.com/test"
}

Note that both JSON strings end up to be parsed correctly in PHP and end up as a normal PHP object

8 Comments

Thank you for your answer. But the problem is not in communication. I need to encode my JSON using HMAC (SHA384). And the result is different in PHP and Ruby when I'm using URL strings. If the string doesn't contain backslash all works fine...
Ah, that's more clear, thanks. So does the PHP implementation (of HMAC(SHA384)) introduces the backslashes in JSON?
PS suggestion: Add your comment to your original question for more clarity ('I need to encode my JSON using HMAC (SHA384). And the result is different in PHP and Ruby when I'm using URL strings. If the string doesn't contain backslash all works fine')
yes, PHP implementation introduces the backslashes. JSON using by PHP looks so {"url":"http:\/\/example.com\/test"} while Ruby's JSON is {"url":"http:\\/\\/example.com\\/test"}
if you use the option: JSON_UNESCAPED_SLASHES the backslashes won't be generated. Like so: echo json_encode(array("url" => "example.com/test"), JSON_UNESCAPED_SLASHES); result ==> {"url":"example.com/test"}
|
0

Try like below

hash = {"url":"http:\\/\\/example.com\\/test"}
hash[:url] = hash[:url].delete("\\")
hash.to_json  #"{\"url\":\"http://example.com/test\"}"

Hope it will helps you

2 Comments

Yes, but this removes all backslashes from the string. While I need to keep ONE backslash. For example, if the string looks like this "qwe\\/123" I need to get "qwe\/123".
There is two back slash on before and after the string

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.