Question

I'm using RoboSpice with Spring for Android and would like to persist a JSON array of objects with OrmLite. GSON is used for the JSON marshalling. With the default caching everything works as expected. But OrmLite doesn't seem to like the array of objects.

This is a simplified version of the JSON:

[{"id": 1, "title": "Test 1"},{"id": 2, "title": "Test 3"},{"id": 3, "title": "Test 3"}]

I would like to persist this in the following object:

@DatabaseTable
public class Foo {
    @DatabaseField(id = true)
    private int id;
    @DatabaseField
    private String title;

    // getters and setters
    ...
}

Based on the RoboSpice OrmLite example I've created the following GsonSpringAndroidSpiceService class to add the OrmLite CacheManager. This is where the problem starts.

public class CustomGsonSpringAndroidSpiceService extends GsonSpringAndroidSpiceService
{
    @Override
    public CacheManager createCacheManager(Application application)
    {
        // add persisted classes to class collection
        List<Class<?>> classCollection = new ArrayList<Class<?>>();
        classCollection.add(Foo.class);

        // init
        CacheManager cacheManager = new CacheManager();
        cacheManager.addPersister(new InDatabaseObjectPersisterFactory(
            application, new RoboSpiceDatabaseHelper(
                application, "database.db", 1), classCollection));
        return cacheManager;
    }
}

This results in the following error:

RequestProcessor.java:174(22356): java.lang.RuntimeException: Class [Lcom.example.model.Foo; is not handled by any registered factoryList

When I change classCollection.add(Foo.class); to classCollection.add(Foo[].class); I get the following error:

RequestProcessor.java:174(22601): 14:42:23.112 pool-5-thread-1 An unexpected error occured when processsing request CachedSpiceRequest [requestCacheKey=foo, cacheDuration=-1, spiceRequest=com.example.app.FooRequest@4055df40]
RequestProcessor.java:174(22601): java.lang.IllegalArgumentException: No fields have a DatabaseField annotation in class [Lcom.example.app.model.Foo;

Anyone an idea how to handle a JSON array with the OrmLite CacheManager ?

Était-ce utile?

La solution

I've found a work around to this problem. I added an extra result object which holds the array of objects. Off course this is only possible if you are able to manipulate the JSON. Still not really happy with this because I have introduce an useless class to my model.

So my the JSON looks like:

{ 
    "id": 1,
    "result":[{"id": 1, "title": "Test 1"},{"id": 2, "title": "Test 3"},{"id": 3, "title": "Test 3"}]
}

And I added the following class to hold the JSON result:

@DatabaseTable
public class FooResult {
    @DatabaseField(id = true)
    private int id;
    @ForeignCollectionField(eager = false)
    private Collection<Foo> result;

    // getters and setters
    ...
}

Also added the foreign relation the the Foo class:

@DatabaseTable
public class Foo {
    @DatabaseField(id = true)
    private int id;
    @DatabaseField
    private String title;
    @DatabaseField(foreign = true)
    private FooResult result;

    // getters and setters
    ...
}

Autres conseils

I have found the way which works for me. I did not changed my json.

@DatabaseTable(tableName = SampleContract.Contributor.TABLE)
public class Contributor {

    @DatabaseField(generatedId = true, columnName = SampleContract.Contributor._ID)
    private int id;

    @DatabaseField(columnName = SampleContract.Contributor.LOGIN)
    public String login;

    @DatabaseField(columnName = SampleContract.Contributor.CONTRIBUTIONS)
    public int contributions;

    @DatabaseField(foreign = true)
    private ContributorsResult result;

    @SuppressWarnings("serial")
    @DatabaseTable(tableName = "contributor_list")
    public static class ContributorsResult extends ArrayList<Contributor> {

        @DatabaseField(id = true)
        private int id = 0;


        @ForeignCollectionField(eager = false)
        private Collection<Contributor> result = this;

        public Collection<Contributor> getResult() {
            return result;
        }

        public void setResult(Collection<Contributor> result) {
            if (result != null) {
                this.clear();
                this.addAll(result);
            }
        }

    }


}

I struggled with this the whole of today and finally figured how to save a JSON array into a SQLite database using Robospice Spring Android without modifying the JSON.

This is my post JSON array returned from my server:

[
{
"id": "5573547af58cd75df03306cc",
"name": "Simon",
"postheader": "First Post"
},
{
"id": "55735475f58cd75df03306cb",
"name": "Tyron",
"postheader": "Second Post"
}
]

This is similar to the JSON in this question:

[{"id": 1, "title": "Test 1"},{"id": 2, "title": "Test 3"},{"id": 3, "title": "Test 3"}]

Step 1:

You will need to create 2 classes on the android side.

One will be the normal object that you want to save from the array. In my case, I have an object called "Post" and my server returns an array of "Post".

@DatabaseTable(tableName = "post")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Post implements Serializable {
    @DatabaseField(id = true)
    private String id;
    @DatabaseField
    private String name;
    @DatabaseField
    private String postheader;
    @DatabaseField(foreign = true,foreignAutoCreate = true,foreignAutoRefresh = true)
    private EmbedPost posts;

The other object will be a wrapper object that wraps over the array, I called mine "EmbedPost".

@DatabaseTable
public class EmbedPost implements Serializable {
    @DatabaseField(allowGeneratedIdInsert=true, generatedId=true)
    private int ID;
    @ForeignCollectionField(eager = false)
    private Collection<Post> posts;

By defining an int called ID in my EmbedPost class, I'm effectively creating a object that if I converted to JSON would look like this:

{"id": 1, "posts": [
{
"id": "5573547af58cd75df03306cc",
"name": "Simon",
"postheader": "First Post"
},
{
"id": "55735475f58cd75df03306cb",
"name": "Tyron",
"postheader": "Second Post"
}
]}

This is effectively a JSON string that looks very similar to what Uipko used in his solution.

{ 
    "id": 1,
    "result":[{"id": 1, "title": "Test 1"},{"id": 2, "title": "Test 3"},{"id": 3, "title": "Test 3"}]
}

Step 2:

You now persist it using SpringAndroidSpiceService.

public class AndroidSpiceService extends SpringAndroidSpiceService {
    private static final int WEBSERVICES_TIMEOUT = 10000;

    @Override
    public CacheManager createCacheManager( Application application ) {
        CacheManager cacheManager = new CacheManager();
        List< Class< ? >> classCollection = new ArrayList< Class< ? >>();

        // add persisted classes to class collection
        classCollection.add(EmbedPost.class);
        classCollection.add( Post.class );
        // init
        RoboSpiceDatabaseHelper databaseHelper = new RoboSpiceDatabaseHelper( application, "sample_database.db", 3);
        InDatabaseObjectPersisterFactory inDatabaseObjectPersisterFactory = new InDatabaseObjectPersisterFactory( application, databaseHelper, classCollection );
        cacheManager.addPersister( inDatabaseObjectPersisterFactory );
        return cacheManager;
    }

    @Override
    public RestTemplate createRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        // set timeout for requests

        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setReadTimeout( WEBSERVICES_TIMEOUT );
        httpRequestFactory.setConnectTimeout( WEBSERVICES_TIMEOUT );
        restTemplate.setRequestFactory( httpRequestFactory );

        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        final List<HttpMessageConverter< ? >> listHttpMessageConverters = restTemplate.getMessageConverters();

        listHttpMessageConverters.add( mappingJackson2HttpMessageConverter  );
        restTemplate.setMessageConverters( listHttpMessageConverters );
        return restTemplate;
    }

}

Be sure to add both the EmbedPost.class and Post.class to your classCollection as ORMLite cannot do its work without you persisting both. You had used ForeignKeys when you wrote up your objects and these foreign keys have to tie to something so therefore both classes must persist.

If you run into trouble, try using the logcat to figure it out. You might have to read all the messages and not just the error ones. See my post here to see how to read all logcat messages:

Robospice storing object that extends ArrayList in database via Ormlite

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top