This repository includes a sample parser for JSON API which is a specification for build JSON APIs.
It is not a fully featured parser yet (hence no maven artifact). It is rather a supplementary document for my blog post about JSON API and how well it works for building offline ready mobile apps.
APIs which return objects nested are hard to handle on the client side because you cannot map it directly to an SQL database w/o massaging your data or using a relational object mapping solution. In my opinion (any many others), relational object mapping is not good for client side development because it either means you are creating more objects than you need or you are implementing mines into your codebase which may trigger main thread queries or both.
JSONAPI specs are great because they send the data to the client as a giant map with references between objects which is very straightforward to persist to an SQL database as is. Then you just fetch necessary data for your views as view models.
Please check the blog post for more details.
Assume you have a GSON that looks like this.
You can model it as follows:
public class Article {
@ResourceId // This is the ID field for the object
String id;
String title;
@Relationship("author") // This is a relationship
String authorId;
@ResourceLink("self") // A link identifier for this object
String selfUrl;
@Relationship("comments") // A relationship can be a lsit of String ids
List<String> commentIds;
@ResourceLink // All links for the Resource as defined in http://jsonapi.org/format/#document-links
JsonApiLinks allLinks;
}
public class Author {
@ResourceId
String id;
@SerializedName("first-name")
String firstName;
@SerializedName("last-name")
String lastName;
@SerializedName("twitter")
String twitter;
}
public class Comment {
@ResourceId
String id;
String body;
@Relationship("author")
String authorId;
}
The you can register this library to your GsonBuilder
via:
static JsonApiResourceDeserializer<Author> AUTHOR_DESERIALIZER = new JsonApiResourceDeserializer<Author>("authors", Author.class);
static JsonApiResourceDeserializer<Article> ARTICLE_DESERIALIZER = new JsonApiResourceDeserializer<Article>("articles", Article.class);
static JsonApiResourceDeserializer<Comment> COMMENT_DESERIALIZER = new JsonApiResourceDeserializer<Comment>("comment", Comment.class);
JsonApiDeserializer.register(new GsonBuilder(), AUTHOR_DESERIALIZER, ARTICLE_DESERIALIZER, COMMENT_DESERIALIZER).create();
And finally, usage:
JsonApiResponse<Article[]> response = gson.fromJson(json, new TypeToken<JsonApiResponse<Article[]>>(){}.getType());
The response class has 3 main parts:
T getData() // the data field represented in the response
Map<String, Map<String, ?>> getIncluded() // the data passed in the included array
JsonApiLinks getLinks() // the links object returned in the response
List<JsonApiError> getErrors() // list of errors in the response
And it also provides convenience methods to get data from the response:
<K> Map<String, K> getIncluded(Class<K> type) // get all items of the given class that was registered above
<K> K getIncluded(Class<K> type, String id) // get a single item from the included array
For instance, for the example above, you can get the author of the first article as follows:
JsonApiResponse<Article[]> response = gson.fromJson(json, new TypeToken<JsonApiResponse<Article[]>>(){}.getType());
Article firstArticle = response.getData()[0];
Author author = response.getIncluded(Author.class, firstArticle.getAuthorId());
The best part of this structure is that, you only need 1 method that can get any JSONApi response and write everything included there into your model.
void saveData(final JsonApiResponse response) {
db.inTransaction(() -> {
for (Map<String, Map<String, ?>> entry : response.getIncluded()) {
Model model = findModelForType(entry.getValue());
model.saveItems(entry.getValue().values());
}
}
}
Please check the blog post how this helps writing an offline app when combined with ViewModels.
This is not an official Google product. It is just owned by Google.