0

Hi I am passing an array of ids in curl command in ruby application via %x(), but I am unable to add those throwing error as :

JSON_PARSING_ERROR: Unexpected character (d) at position 

Array : @m = ["d_sample_id1","d_sample_id2"]

Command used :

%x(curl --header 'Authorization: key=keynotmentioned' --header 'Content-Type: application/json' https://android.googleapis.com/gcm/send -d '{"registration_ids": "#{@m}", "collapse_key": "Turn", "data": { "title": "test", "sound": true,"body": "test", "badge": "1" , "content_available": 1 ,"url": ""}}')

Showing json parse error , can you help me out from this ? Thanx in advance.

1
  • I just tried your command, exactly as shown above, and it worked fine for me. Are you sure this is the line which generates the error? Commented May 19, 2016 at 12:40

1 Answer 1

2

First let's figure out what went wrong. Here's an approximation of your code trimmed for brevity:

ids = ["d_sample_id1","d_sample_id2"]
puts %(curl ... -d '{"registration_ids": "#{ids}"}')

Here I'm using puts %() instead of %x(...) to just print the string instead of executing it. Here's the output:

curl ... -d '{"registration_ids": "["d_sample_id1", "d_sample_id2"]"}'

Spot the problem? It's here: "["d_.... You've put your array inside double-quotes. When the JSON parser sees the first double-quote it thinks "ah, this is the beginning of a string". Then it gets to the next double-quote, right after the square bracket, and thinks, "and here is the end of the string." But the very next character is the letter d, which doesn't make any sense right after a string, so the parser throws an error and calls it a day.

You could fix that by removing the extra quotation marks. But it would be a band-aid at best, for this reason: It would only work incidentally. When you use string interpolation in Ruby, Ruby calls to_s on the expression inside #{...}. But it's only incidental that when you call to_s on an array of Ruby strings you get valid JSON. What if the expression is a Hash, or a Date? String interpolation will fail you.

But even if you know you've just got an array of Strings, interpolation will fail you for a second reason: You're building command-line arguments, which need to be properly escaped. Take this for example:

arr = ["I can't even"]
%(curl ... -d '#{arr}')

Spot the problem? This is going to try to execute the following:

curl ... -d '["I can't even"]'

Spot the problem? This time you've got mismatched single-quotes.

What a nightmare!

This leads us to two important rules of thumb:

  1. Don't build JSON with string concatenation/interpolation unless you enjoy pain and suffering.

  2. Be very careful using string concatenation/interpolation to build command-line arguments unless you enjoy pain and suffering.

Fortunately we don't have to worry about our JSON being formatted properly or our command-line arguments being properly escaped, because much smarter people than us have already worried about those things and put the solutions right into Ruby's standard library, in the delightful JSON and Shellwords modules. And so:

require "json"
require "shellwords"

ids = [ "d_sample_id1", "d_sample_id2" ]

data = {
  registration_ids: ids,
  collapse_key: "Turn",
  data: { 
    title: "test",
    sound: true,
    body: "test",
    badge: "1" ,
    content_available: 1,
    url: ""
  }
}

json_arg = Shellwords.escape(data.to_json)

%x(curl \
  --header 'Authorization: key=keynotmentioned' \
  --header 'Content-Type: application/json' \
  "https://android.googleapis.com/gcm/send" \
  -d #{json_arg}
)

As a bonus, being able to set up our data as a Ruby Hash makes it way more readable, maintainable, and testable.

Sign up to request clarification or add additional context in comments.

3 Comments

It worked for me but i used escape gem instead of Shellwords gem as my application is a lower version ruby
What version? Shellwords isn't a gem; it's in the standard library and has been since Ruby 1.8.7 (2008). If you're using something older than that you oughtn't; versions that old don't receive security patches anymore.
I am using ruby 1.8.6.

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.