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"}
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('/', '\/') # "\/"