17

i am using FCM for push messages and handling all incoming push notification in onMessageReceived. Now the issue is with parsing nested json that comes inside this function remoteMessage.getData()

I have following block coming as a push notification in device. content of data payload could be varied here it is dealer later on it can be productInfo

{
  "to": "/topics/DATA",
  "priority": "high",
  "data": {
    "type": 6,
    "dealerInfo": {
      "dealerId": "358",
      "operationCode": 2
    }
  }
}

this how i am parsing it

 if(remoteMessage.getData()!=null){

        JSONObject object = null;
        try {
            object = new JSONObject(remoteMessage.getData());       

        } catch (JSONException e) {
            e.printStackTrace();
        }


    }

now i am getting data with blackslashes as remoteMessage.getData() returns Map<String,String> so probably my nested block is being converted in string not sure though.

{
  "wasTapped": false,
  "dealerInfo": "{\"dealerId\":\"358\",\"operationCode\":2}",
  "type": "6"
}

and if i write object = new JSONObject(remoteMessage.getData().toString()); then it got failed with following notification

{
  "to": "regid",
  "priority": "high",
  "notification" : {
      "body": "Message Body",
      "title" : "Call Status",
      "click_action":"FCM_PLUGIN_ACTIVITY"
   },
  "data": {
    "type": 1,
     "callNumber":"ICI17012702",
     "callTempId":"0",
      "body": "Message Body",
      "title" : "Call Status"
  }
}

error i get is

> org.json.JSONException: Unterminated object at character 15 of
> {body=Message Body, type=1, title=Call Status, callNumber=ICI17012702,
> callTempId=0}
rafsanahmad007
  • 23,683
  • 6
  • 47
  • 62
Hunt
  • 8,215
  • 28
  • 116
  • 256

6 Answers6

16

try this code:

public void onMessageReceived(RemoteMessage remoteMessage)
    {
        Log.e("DATA",remoteMessage.getData().toString());
        try
        {
            Map<String, String> params = remoteMessage.getData();
            JSONObject object = new JSONObject(params);
            Log.e("JSON OBJECT", object.toString());
            String callNumber = object.getString("callNumber");
            //rest of the code
      }
   }

Also make sure your JSON is valid use This

rafsanahmad007
  • 23,683
  • 6
  • 47
  • 62
  • not possible this way how would i access the dealerInfo part – Hunt Feb 16 '17 at 13:52
  • The nested object still remains a string in this case. – Abhinav Manchanda Mar 28 '17 at 08:04
  • 3
    Well, if you have a JSON object with one level of nesting, it works perfectly. As soon as you have two or more levels of nesting, FCM converts the nested JSON object into a string. When you parse it and print it, it becomes an escaped string, and not a JSON object. The exact code that you've written here gives that issue. – Abhinav Manchanda Mar 28 '17 at 08:34
  • And I agree that it's an FCM issue, not an issue with your thought process, since FCM returns the whole JSON object as a string value of the map, but still it doesn't work. – Abhinav Manchanda Mar 28 '17 at 08:36
7

Faced this issue when migrating from GCM to FCM.

The following is working for my use case (and OP payload), so perhaps it will work for others.

JsonObject jsonObject = new JsonObject(); // com.google.gson.JsonObject
JsonParser jsonParser = new JsonParser(); // com.google.gson.JsonParser
Map<String, String> map = remoteMessage.getData();
String val;

for (String key : map.keySet()) {
    val = map.get(key);
    try {
        jsonObject.add(key, jsonParser.parse(val));
    } catch (Exception e) {
        jsonObject.addProperty(key, val);
    }
}

// Now you can traverse jsonObject, or use to populate a custom object:
// MyObj o = new Gson().fromJson(jsonObject, MyObj.class)
gonga
  • 301
  • 3
  • 5
0

I have changed to

JSONObject json = new JSONObject(remoteMessage.getData());

from

JSONObject json = new JSONObject(remoteMessage.getData().toString());

and work fine.

rafsanahmad007
  • 23,683
  • 6
  • 47
  • 62
priyanka
  • 9
  • 3
0

Since dealerInfo is parsed as string and not an object, create a new JSONObject with the string

JSONObject dealerInfo = new JSONObject(object.getString("dealerInfo"));
String dealerId = dealerInfo.getString("dealerId");
String operationCode = dealerInfo.getString("operationCode");
kehers
  • 4,076
  • 3
  • 30
  • 31
0

I didn't want to add GSON (as I use moshi) to make it working, so I made Kotlin method to form json string from remoteMessage's map, tested this on one example, so don't forget to test this implementation before using:

override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        super.onMessageReceived(remoteMessage)

        var jsonString = "{"

        remoteMessage?.data?.let {
            val iterator = it.iterator()

            while (iterator.hasNext()) {
                val mapEntry = iterator.next()
                jsonString += "\"${mapEntry.key}\": "
                val value = mapEntry.value.replace("\\", "")
                if (isValueWithoutQuotes(value)) {
                    jsonString += value
                } else {
                    jsonString += "\"$value\""
                }

                if (iterator.hasNext()) {
                    jsonString += ", "
                }
            }
        }

        jsonString += "}"


        println(jsonString)
    }

    private fun isValueWithoutQuotes(value: String):Boolean{
       return (value == "true" || value == "false" || value.startsWith("[") || value.startsWith("{") || value == "null" || value.toIntOrNull() != null )
    }

Edit:

Even better approach is to form FCM data like:

notificationType: "here is ur notification type"
notificationData: {
//here goes ur data
}

That way we can retreive both values from map.

remoteMessage?.data?.let {
    it["notificationData"]?.let {
         jsonString = it.replace("\\", "")
    }
}

We got clear json without "playing" around. And we can then use notificationType to convert json to the object that we need (as several notification data types can be passed sometimes)

Janusz Hain
  • 597
  • 5
  • 18
  • Wouldn't it be easier to make a JSONObject just like the accepted response, and them calling toString() on it to get the full json? – Andre Romano Nov 10 '18 at 00:09
  • @AndreRomano it depends, as someone said under the accepted response - accepted response works, but not for nested objects. My solutions works for nested functions and you can have several object types (as backend can send different objects, not only one type) – Janusz Hain Nov 13 '18 at 14:11
0
 @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.d(TAG, "From: " + remoteMessage.getFrom());
        if (remoteMessage.getData().size() > 0) {
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());

//            if (/* Check if data needs to be processed by long running job */ true) {
//                // For long-running tasks (10 seconds or more) use WorkManager.
//                scheduleJob();
//            } else {
//                // Handle message within 10 seconds
//                handleNow();
//            }

        }

        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
            sendNotification(remoteMessage.getNotification().getBody());
        }

    }