简体   繁体   中英

How to keep single backslash in Ruby string after to_json formating?

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

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.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM