2012-12-19 15 views
7

Ho ereditato un codice che consente di risparmiare il nostro stato di applicazione come JSON utilizzando GSON, e poi lo legge con fromJson.RuntimeException in GSON analisi JSON: Impossibile richiamare protetto java.lang.Classloader() senza args

Gson gson = createGson(); 
gson.fromJson(objString, myClass); 

Uno dei campi salvati è uno Location. Purtroppo, molto raramente l'analisi di quei dati salvati non riesce perché la mia posizione salvata include una mClassLoader nelle sue mExtras, e la biblioteca GSON non riesce a creare il ClassLoader con questo errore:

RuntimeException: Failed to invoke protected java.lang.ClassLoader() with no args

Qualcuno sa perché un ClassLoader è essere incluso negli extra per la mia posizione e se dovrebbe finire nella rappresentazione JSON?

Suppongo di poter risolvere questo problema semplicemente salvando i campi chiave dall'oggetto Posizione singolarmente (ad esempio longitudine, latitudine, altitudine, tempo, precisione), ma sarebbe bello salvare l'oggetto Posizione, se possibile.

ho visto c'è un oggetto ExclusionStrategy ho potuto utilizzare per escludere i campi, ma non ero sicuro se potrebbe/dovrebbe utilizzare che per escludere i gli extra da dentro la mia posizione ...

Cordiali saluti, ecco i dati JSON per il mio oggetto Location (con la longitudine e la latitudine modificati per nascondere me):

{ 
    <snip> 
    "lastKnownLocation": { 
     "mResults": [ 
      0, 
      0 
     ], 
     "mProvider": "gps", 
     "mExtras": { 
      "mParcelledData": { 
       "mOwnObject": 1, 
       "mObject": 5525040 
      }, 
      "mClassLoader": { 
       "packages": {} 
      }, 
      "mMap": {}, 
      "mHasFds": false, 
      "mFdsKnown": true, 
      "mAllowFds": true 
     }, 
     "mDistance": 0, 
     "mTime": 1354658984849, 
     "mAltitude": 5.199999809265137, 
     "mLongitude": -122.4376, 
     "mLon2": 0, 
     "mLon1": 0, 
     "mLatitude": 37.7577, 
     "mLat1": 0, 
     "mLat2": 0, 
     "mInitialBearing": 0, 
     "mHasSpeed": true, 
     "mHasBearing": false, 
     "mHasAltitude": true, 
     "mHasAccuracy": true, 
     "mAccuracy": 16, 
     "mSpeed": 0, 
     "mBearing": 0 
    }, 
    <snip> 
} 

Ecco un esempio ciò che il mExtras contiene quando il codice non va in crash:

"mExtras": { 
    "mParcelledData": { 
     "mOwnsNativeParcelObject": true, 
     "mNativePtr": 1544474480 
    }, 
    "mHasFds": false, 
    "mFdsKnown": true, 
    "mAllowFds": true 
} 

risposta

19

Il problema è che si sta tentando di convertire direttamente una classe fornita dal sistema (Location) in JSON. E, come vedete, vi imbattete in problemi con la serializzazione di elementi interni specifici dello stato/Java. JSON è inteso come un modo semi-generico per passare informazioni in giro.

Non è possibile utilizzare facilmente l'annotazione @Expose perché non è la propria classe; che richiederebbe la modifica del codice sorgente per Location o tramite un abbastanza ampio processo di aggiungerli in fase di esecuzione utilizzando jassist (si veda: http://ayoubelabbassi.blogspot.com/2011/01/how-to-add-annotations-at-runtime-to.html)

Guardando la classe Location, avrei semplicemente creare un costume GSON Serializzatore e Deserializzatore ed essere fatto con esso. Ciò che realmente ti interessa sono i dati GPS, non quelli interni della classe stessa. Basta costruire il JSON contenente le informazioni necessarie nel serializzatore usando i getter, quindi nel Deserializer si crea una nuova istanza di Location e si popola con le informazioni dal JSON fornito utilizzando i setter pubblici.

class LocationSerializer implements JsonSerializer<Location> 
{ 
    public JsonElement serialize(Location t, Type type, 
           JsonSerializationContext jsc) 
    { 
     JsonObject jo = new JsonObject(); 
     jo.addProperty("mProvider", t.getProvider()); 
     jo.addProperty("mAccuracy", t.getAccuracy()); 
     // etc for all the publicly available getters 
     // for the information you're interested in 
     // ... 
     return jo; 
    } 

} 

class LocationDeserializer implements JsonDeserializer<Location> 
{ 
    public Location deserialize(JsonElement je, Type type, 
           JsonDeserializationContext jdc) 
          throws JsonParseException 
    { 
     JsonObject jo = je.getAsJsonObject(); 
     Location l = new Location(jo.getAsJsonPrimitive("mProvider").getAsString()); 
     l.setAccuracy(jo.getAsJsonPrimitive("mAccuracy").getAsFloat()); 
     // etc, getting and setting all the data 
     return l; 
    } 
} 

Ora nel codice si utilizza GsonBuilder e registrare le classi:

... 
GsonBuilder gsonBuilder = new GsonBuilder(); 
gsonBuilder.registerTypeAdapter(Location.class, new LocationDeserializer()); 
gsonBuilder.registerTypeAdapter(Location.class, new LocationSerializer()); 
Gson gson = gsonBuilder.create(); 
... 

Questo dovrebbe più o meno prendersi cura di esso.

+0

Bella risposta, grazie. Pensavo di aver verificato che Serializable implementasse l'oggetto Location, ma non è così, quindi non sorprende che il nostro codice sia stato violato! –

+0

Ora testato e la soluzione funziona alla grande. Salvando i nomi dei campi con gli stessi nomi utilizzati dal sistema ("mProvider", "mAccuracy" ecc.) È compatibile con i vecchi dati dell'applicazione contenenti l'extra mClassLoader utilizzato per generare un'eccezione. –

+0

Fantastico. Sono contento di poterti aiutare. –

2

consideri per creare un oggetto personalizzato che i modelli tuo GSON, l'analisi del contenuto oggetto per oggetto e campo per campo

qualcosa come

JSONObject lastKnownLocation = obj.getJSONObject("lastKnownLocation"); 
JSONArray mResults = lastKnownLocation.getJSONArray("mResults"); 

etc... 
MyGSON mg=new MyGSON(lastKnownLocation, mResults etc....); 

Così si possono ottenere il pieno controllo del parsing, e aggiungendo un blocco try \ catch nella parte critica di mExtra è possibile escludere il blocco o gestire facilmente l'eccezione desiderata.

Problemi correlati