问题
For learning purposes i'm creating an android app by using Realm and the Edinburg Festival Api. It's going pretty well except for one problem.
I'm using the following to convert the retrieved JSON to RealmObjects:
public void onResponse(final String response) {
realm.executeTransactionAsync(new Realm.Transaction(){
@Override
public void execute(Realm realm) {
// Update our realm with the results
parseImages();
realm.createOrUpdateAllFromJson(Festival.class, response);
}
}
}
This works fine except for one field, the images. The image part of the JSON:
"images": {
"031da8b4bad1360eddea87e8820615016878b183": {
"hash": "031da8b4bad1360eddea87e8820615016878b183",
"orientation": "landscape",
"type": "hero",
"versions": {
"large-1024": {
"height": 213,
"mime": "image/png",
"type": "large-1024",
}
"width": 1024
}
}
The problem here is the hash inside the image object. I have no clue how to handle this. The hash is different for every festival. Would it be possible to to make a custom JSON deserializer in my RealmObject?
Last code sample is my current model:
public class Festival extends RealmObject {
@PrimaryKey
public String title;
RealmList<Image> images;
public String description_teaser;
public String description;
public String genre;
public String age_category;
public String website;
public RealmList<Performance> performances;
public int votes;
}
I'm aware my PK is not optimal but this is still just testing to get the images working and i needed to set a PK for migrating.
Any tips are welcome, cheers :)
Update
Added the image model:
public class Image extends RealmObject {
public String hash;
public String orientation;
public String type;
RealmList<Version> versions;
}
Update 2
My attempt to parse the images before calling realm.createOrUpdateAllFromJson(Festival.class, response);
private void parseImages(String jsonString) throws JSONException {
JSONArray jsonArr = new JSONArray(jsonString);
for(int i = 0; i < jsonArr.length(); i++){
JSONObject jsonObj = jsonArr.getJSONObject(i);
JSONObject images = (JSONObject)jsonObj.get("images");
Iterator<String> iter = images.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
JSONObject value = json.get(key);
realm.createOrUpdateObjectFromJson(Image.class,value);
} catch (JSONException e) {
// Something went wrong!
}
}
}
}
Update 3
I created a function that cleans up the broken JSON i get from the API. It ain't very nice but it works for now. it removes the hashes and the wierd versions and just places them both in a array. I'm sure it could be more efficiently written but i'll just go with this so i can move on with the rest of my app for now. See my own answer.
回答1:
my own temporary solution:
/**
* Function to fix the json coming from the Festival API
* This is a bit more complicated then it needs to be but realm does not yet support @Serializedname
* It removes the "large-1024" (and simllar) object and places the versions in a JSON version array
* Then it removes the hashes and creates and images array. The JsonArray can now be parsed normally :)
*
* @param jsonString Result string from the festival api
* @return JSONArray The fixed JSON in the form of a JSONArray
* @throws JSONException
*/
private JSONArray cleanUpJson(String jsonString) throws JSONException {
JSONArray json = new JSONArray(jsonString);
for(int i = 0; i < json.length(); i++){
// We store the json Image Objects in here so we can remove the hashes
Map<String,JSONObject> images = new HashMap<>();
JSONObject festivalJson = json.getJSONObject(i);
JSONObject imagesJson = (JSONObject)festivalJson.get("images");
// Iterate each hash inside the images
Iterator<String> hashIter = imagesJson.keys();
while (hashIter.hasNext()) {
String key = hashIter.next();
try {
final JSONObject image = imagesJson.getJSONObject(key);
// Remove the version parents and map them to version
Map<String, JSONObject> versions = new HashMap<>();
JSONObject versionsJsonObject = image.getJSONObject("versions");
// Now iterate all the possible version and map add to the hashmap
Iterator<String> versionIter = versionsJsonObject.keys();
while(versionIter.hasNext()){
String currentVersion = versionIter.next();
versions.put(currentVersion,versionsJsonObject.getJSONObject(currentVersion));
}
// Use the hashmap to modify the json so we get an array of version
// This can't be done in the iterator because you will get concurrent error
image.remove("versions");
Iterator hashMapIter = versions.entrySet().iterator();
JSONArray versionJsonArray = new JSONArray();
while( hashMapIter.hasNext() ){
Map.Entry pair = (Map.Entry)hashMapIter.next();
versionJsonArray.put(pair.getValue());
}
image.put("versions",versionJsonArray);
Log.d(LOG_TAG,image.toString());
} catch (JSONException e) {
e.printStackTrace();
}
images.put(key,imagesJson.getJSONObject(key));
}
// Now let's get rid of the hashes
Iterator hashMapIter = images.entrySet().iterator();
JSONArray imagesJsonArray = new JSONArray();
while( hashMapIter.hasNext() ){
Map.Entry pair = (Map.Entry)hashMapIter.next();
imagesJsonArray.put(pair.getValue());
}
festivalJson.put("images", imagesJsonArray);
}
return json;
}
Hope it helps someone :) But sure ain't neat.
回答2:
Due to how the keys are dynamic in this JSON (why isn't this an array? Whoever designed this API had no idea what they were doing), you'll have to manually parse the object up to the point of the hash key:
JSONObject jsonObj = new JSONObject(jsonString);
JSONObject images = (JSONObject)jsonObj.get("images");
Iterator<String> iter = images.keys();
while (iter.hasNext()) {
String key = iter.next();
try {
JSONObject value = json.get(key);
realm.createOrUpdateObjectFromJson(Image.class, value.toString());
} catch (JSONException e) {
// Something went wrong!
}
}
来源:https://stackoverflow.com/questions/40535188/custom-deserializer-for-realmobject