1

I'm attempting to save my array of players into a JSONArray, convert it to a string, place it into SharedPreferences then convert it back. The JSONArray I'm getting back when reloading my app with my current attempt is throwing runtime exceptions/ClassCast Exceptions if I attempt to access any of the Player instances within the array.

Where am I going wrong?

JSONArray savedPlayers;
SharedPreferences prefs;
@Override
public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.indie);

    savedPlayers = new JSONArray();

    prefs = getSharedPreferences("appData", 0);
    String jsonString = prefs.getString("playerString", null);

    //Restores playerArray if any players have been saved in the past
    if(jsonString != null)
    {
        try 
        {

            savedPlayers = new JSONArray(JSONString);
            MyListView.players.clear();
            MyListView.ids.clear();
            for(int i = 0; i < savedPlayers.length(); i++)
            {
                MyListView.players.add(((Player) savedPlayers.get(i)).getName()); //Error
                MyListView.ids.add(((Player) savedPlayers.get(i)).getSaveId());
            }
        } 
        catch (JSONException e) 
        {
            e.printStackTrace();
        }
    }
}

//If there are savedPlayers converts the JSONArray to a string and saved it within SharedPreferences
@Override
protected void onPause() 
{
    if(savedPlayers != null)
    {
        SharedPreferences.Editor editor = getSharedPreferences("appData", 0).edit();
        String jsonString = savedPlayers.toString();
        editor.putString("playerString", jsonString).commit();
    }
    super.onPause();
}

JSONString:
["en.deco.android.livehud.Player@44eba878","en.deco.android.livehud.Player@44ebec68"]

Logcat:

04-16 22:31:13.765: ERROR/AndroidRuntime(7496): Uncaught handler: thread main exiting due to uncaught exception
04-16 22:31:13.785: ERROR/AndroidRuntime(7496): java.lang.RuntimeException: Unable to start activity ComponentInfo{en.deco.android.livehud/en.deco.android.livehud.GUI}: java.lang.ClassCastException: java.lang.String
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2496)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread.access$2200(ActivityThread.java:119)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.os.Looper.loop(Looper.java:123)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread.main(ActivityThread.java:4363)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at java.lang.reflect.Method.invokeNative(Native Method)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at java.lang.reflect.Method.invoke(Method.java:521)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at dalvik.system.NativeStart.main(Native Method)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496): Caused by: java.lang.ClassCastException: java.lang.String
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at en.deco.android.livehud.GUI.onCreate(GUI.java:102)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
04-16 22:31:13.785: ERROR/AndroidRuntime(7496):     ... 11 more
04-16 22:32:57.775: ERROR/AndroidRuntime(7537): Uncaught handler: thread main exiting due to uncaught exception
04-16 22:32:57.795: ERROR/AndroidRuntime(7537): java.lang.RuntimeException: Unable to start activity ComponentInfo{en.deco.android.livehud/en.deco.android.livehud.GUI}: java.lang.ClassCastException: java.lang.String
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2496)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread.access$2200(ActivityThread.java:119)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.os.Looper.loop(Looper.java:123)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread.main(ActivityThread.java:4363)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at java.lang.reflect.Method.invokeNative(Native Method)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at java.lang.reflect.Method.invoke(Method.java:521)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at dalvik.system.NativeStart.main(Native Method)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537): Caused by: java.lang.ClassCastException: java.lang.String
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at en.deco.android.livehud.GUI.onCreate(GUI.java:102)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
04-16 22:32:57.795: ERROR/AndroidRuntime(7537):     ... 11 more
jnthnjns
  • 8,962
  • 4
  • 42
  • 65
Declan McKenna
  • 4,321
  • 6
  • 54
  • 72
  • Have you printed out `savedPlayers.toString();` to actually look what you are saving? – Blundell Apr 16 '12 at 21:56
  • @Blundell I'll do that now. I'm hoping it will be a JSONString capable of being converted back to it's original form. – Declan McKenna Apr 16 '12 at 22:01
  • JSONString is a bad name for a variable because it follows the naming convention of the JSON* class names. – Thomas Dignan Apr 16 '12 at 22:06
  • @TomDignan Ye looking over the documentation there appears to be something called a JSONString. I'll change it now. Don't think it will affect anything though as I'd have expected a compliler warning and I haven't imported it. – Declan McKenna Apr 16 '12 at 22:08
  • Toms right, and you have a class variable and two instance variables with the same name. Reading your question again the prefs saving probably works. It's the objects within your array your having issues with. What does the LogCat error actually show? – Blundell Apr 16 '12 at 22:08
  • @Blundell java.lang.RuntimeException: Unable to start activity ComponentInfo....: java.lang.ClassCastException java.lang.String Each time it points to any line that tries to access data from the reloaded version of the savedPlayers JSONArray. – Declan McKenna Apr 16 '12 at 22:12
  • Edit your question and paste the LogCat error fully. Also show us the line that is actually creating the error – Blundell Apr 16 '12 at 22:14
  • @Blundell I'm not sure how to copy and paste then logcat via eclipse :|. I'll take a screenshot. and add one of the error causing lines I've been through. – Declan McKenna Apr 16 '12 at 22:16
  • Cool. Either that way or just select the line in eclipse press ctrl+c and you can paste it anywhere :-) – Blundell Apr 16 '12 at 22:17
  • @Blundell Done. I've used the exaxt same line of code prior to conversion and it's worked fine. MyListView is a second activity used for selecting and loading the players. The error code is re-adding the references to the array. – Declan McKenna Apr 16 '12 at 22:24
  • It looks like you're casting each entry in the JSONArray directly to a player: ((Player) savedPlayers.get(i)). You can't do that, instead you have to create a Player from the data contained in each entry of the array. Update your question with the JSON and I may be able to help further. – Ian Newson Apr 16 '12 at 23:09
  • Be careful storing large objects in the `SharedPreferences`. It has a maximum file size, which can mean that your JSON object won't read out correctly. I would suggest using a SQLite database instead. – Paul Lammertsma Apr 16 '12 at 23:09
  • @IanNewson Casting each entry as Player worked prior to the JSON > String > JSON conversion. All entries are players. What do you mean by update the question with the JSON? Thanks – Declan McKenna Apr 16 '12 at 23:22
  • You're creating a JSON string and saving this in the shared preferences, right? If so, could you provide us the JSON string that you have created and stored in the preferences please? – Ian Newson Apr 16 '12 at 23:24
  • @IanNewson How do I print it out? System.out.println() doesn't seem to appear in logcat or the console? – Declan McKenna Apr 16 '12 at 23:36
  • 1
    Use android.until.Log.d: http://developer.android.com/reference/android/util/Log.html – Ian Newson Apr 16 '12 at 23:38
  • @IanNewson Think I found it using System.out, I've posted it up. I'll try your way as well just to be sure. – Declan McKenna Apr 16 '12 at 23:43
  • 1
    Your JSON isn't correct, it's simply a to string representation of each Player object, so it won't be possible to turn this back in to the original objects. You need to serialize the properties of your player objects. You may want to use GSON instead of JSONReader, as it's easier to use. – Ian Newson Apr 16 '12 at 23:51
  • @IanNewson I'm going to use GSON but I'm almost sure your right. Feel free to put that down as an answer. I entered my Player objects into the JSON array when according t9o the documentation they need to be converted into JSONObjects before I do this. http://developer.android.com/reference/org/json/JSONArray.html – Declan McKenna Apr 17 '12 at 12:09

7 Answers7

2

You are currently creating a JSONArray using the toString representation of each Player object, which results in an array of strings which don't contain any information from the fields of the Player itself. If you are going to use the JSONArray/JSONObject classes, then you need to instead create a JSONObject object for each of your players, and add those to the JSONArray. For example, the following code would create a JSONObject, add the name of your player to the object, and then add it to the JSONArray:

JSONObject playerJsonObject = new JSONObject();
playerJsonObject.put("name", player.getName());
jsonArray.put(playerJsonObject);

However, a much simpler way of doing JSON serialization is to use GSON: http://code.google.com/p/google-gson/

Ian Newson
  • 7,679
  • 2
  • 47
  • 80
1

You should consider using Jackson for this.

All your serialization and desirialization code will disappear and your life will be much simpler.

Savvas Dalkitsis
  • 11,476
  • 16
  • 65
  • 104
1
 String jsonString = savedPlayers.toString();

This isn't working. It's calling toString on each object (Player) in your array. The objects in your array have not overridden the toString method and therefore are just returning the space in memory that they are located at:

 en.deco.android.livehud.Player@44eba878

You would need to override toString in your Player class, I don't know what your class looks like but it would go something along the lines of this:

public class Player {
 String name;
 int saveId;

 public Player(String name, int saveId){
   this.name = name;
   this.saveId = saveId;
 }

 public String getName(){
  return name;
 }

 public int getSaveId(){
  return saveId();
 }

 @Override
 public String toString(){
  return "{" + "name:"+ this.name +"," + "saveId:"+ this.saveId +"}";
 }

}

Take a look at the JSON site for a reference of the structure the JSON should be: http://www.json.org/

It is also recommended you override hashcode() when you override toString();

Blundell
  • 75,855
  • 30
  • 208
  • 233
  • Still doesn't work I'm afraid I will likely switch to GSON. I'm pretty positive the problem is that I did not encode the players into JSONObjects before placing them in the JSONArray. Might even stick to JSON just for the sake of getting an definite answer for this question. – Declan McKenna Apr 17 '12 at 12:18
  • *still doesn't work* So when you print out savedPlayers.toString() what does it look like now? – Blundell Apr 17 '12 at 12:21
  • Seems like it's empty now. [] – Declan McKenna Apr 17 '12 at 12:26
  • Ah wait. First time I try and load it's empty. Second time it gives this. ["{name:jim,saveId:0}","{name:jim,saveId:0}"] Weird onPause() should be getting called the second I switch activity. – Declan McKenna Apr 17 '12 at 12:30
  • If I turn the app on and off prior to saving the String is always "[]". – Declan McKenna Apr 17 '12 at 12:33
  • Because your Array doesn't have any players in it. – Blundell Apr 17 '12 at 12:35
  • in onPause try `if(savedPlayers != null && savePlayers.length > 0)` and don't forget now you have something in your sharedPrefs you may need to clear data inbetween redeploys – Blundell Apr 17 '12 at 12:37
  • The log calls aren't coming up on LogCat for me. Still isn't working though. – Declan McKenna Apr 17 '12 at 12:51
  • Thanks for all the help but I'm going to give GSON a shot. This is my first time using serialization and it appears JSON weren't the right choice for a beginner. lol will likely have yet another question when I mess up that. – Declan McKenna Apr 17 '12 at 13:01
0

shouldn't you be converting your string into Array first and then parse as json array ?

Kamal
  • 1,122
  • 11
  • 18
  • JSONArray(java.lang.String source) Construct a JSONArray from a source JSON text. as being a string argument. – Declan McKenna Apr 16 '12 at 22:27
  • Does the above refer to a string? Rereading it, maybe it doesn't. – Declan McKenna Apr 16 '12 at 22:29
  • it says from JSON string source, your string should be in JSON format already, but you are not saving it in JSON format you are saving plain text. So your JSON Array is not able to parse it while reading back.. – Kamal Apr 16 '12 at 22:36
  • He is when you do .toString on a JSONArray is returns a json structured string. Check the API http://www.json.org/javadoc/org/json/JSONArray.html#toString() – Blundell Apr 16 '12 at 22:38
  • @Deco we need more than just the top line, its meaningless – Blundell Apr 16 '12 at 22:47
  • @Blundell Sorry I'm new to this. Posted all of it up. The error line has changed on me, it's now "savedPlayers = new JSONArray(JSONString);" – Declan McKenna Apr 16 '12 at 22:55
0

the native android JSON library is very sensitive to error types that occur should you try and cast inappropriate types. As such each call that adds to you JSON object must have an error handler.

This results in a lot of code and complexity for what should be a really painless process. Fortuneately, there are libraries that make this sort of conversions really easy. I personally recommend the GSON library: http://code.google.com/p/google-gson/

JSON makes it easy to round-trip your data, bot serializing and deserializing your JSON objects into the Java classes used by your app.

Read through the usage page to see, You can accomplish the conversion in as few as 4 lines of code. https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Generic-Types

Also, as an aside, @Kamal makes a good point about converting your string to an array first in java. Or, alternately using GSON, you could convert it from a custom class type. Its really that easy.

Plastic Sturgeon
  • 12,527
  • 4
  • 33
  • 47
0

GSON is pretty good for JSON manipulation:

very same question as yours is answered here:

Can't Convert string to JsonArray

Community
  • 1
  • 1
Mik378
  • 21,881
  • 15
  • 82
  • 180
0

We need to make JSON object first. For example, if resp is a String (for example coming as http response)

JSONObject jsonObject = new JSONObject(resp); 

jsonObject may contains other JSON Objects or JSON array. How to convert the JSON depends on the response. If arraykey is a array inside the JSON objects then we can get list of array by the following way.

JSONArray arr  = jsonObject.getJSONArray("arraykey");

Check the length of arr, if it is greater than 0 then it contains JSON objects or JSON array depending the data. There is a complete example with some explanation about JSON String to JSON array can be found at http://www.hemelix.com/JSONHandling

RotatingWheel
  • 1,023
  • 14
  • 27