Skip to content
This repository was archived by the owner on Nov 13, 2019. It is now read-only.

DataConservancy/jsonapi-converter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

89 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Custom Fork

This fork contains features not yet merged into the upstream develop branch. Specifically it contains bug fixes for:

  • detecting and preventing recursions while resolving relationships (#28)
  • correctly parsing to-many and to-one responses while resolving relationships (#30)
  • supports storing relationship urls as string references (#31)

It is released under the Maven groupId:artifactId org.dataconservancy.jasminb:jsonapi-converter

Custom Release Process

To make a release, have a clean working copy. Run (substituting the proper versions in):

  • mvn release:prepare -DpushChanges=false -Dtag=dataconservancy-local-release-0.3 -DreleaseVersion=0.3 -DdevelopmentVersion=0.4-SNAPSHOT -Dresume=false
  • mvn -e release:perform -Dgoals=deploy -DlocalCheckout=true

If the git logs look good after performing the release, push the changes to origin.

Examples

    @Types("node")
    class Node {
    
      @Id
      String id;
    
      @Relationship(value = "children", resolve = true, relType = RelType.RELATED, strategy = ResolutionStrategy.REF)
      String childrenRel;
    
      // ...
    }
  • supports the modeling of JSON-API "links" objects using the new Links annotation
    @Types("node")
    class Node {
    
      @Id
      String id;
    
      @Links
      Map<String, ?> links;
    
      // ...
    }
  • Collections now expose pagination and meta information via ResourceList class

The instructions for building this branch are the same as the upstream, and are documented below.

jsonapi-converter

JSONAPI-Converter is a library that provides means for integrating with services using JSON API specification.

For information on JSON API specification please see: http://jsonapi.org/format/

Besides providing support for request/response parsing, library provides a retrofit plugin.

Library is using Jackson (https://github.com/FasterXML/jackson-databind) for JSON data parsing.

Including the library in your project
<dependency>
  <groupId>com.github.jasminb</groupId>
  <artifactId>jsonapi-converter</artifactId>
  <version>0.2</version>
</dependency>

In case you want to use current SNAPSHOT version of the project, make sure to add sonatype repository to your pom:

<repositories>
    <repository>
        <id>oss-sonatype</id>
        <name>oss-sonatype</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

Than to add dependency:

<dependency>
  <groupId>com.github.jasminb</groupId>
  <artifactId>jsonapi-converter</artifactId>
  <version>0.3-SNAPSHOT</version>
</dependency>
Writing your model classes

When writing models that will be used to represent requests and responses, one needs to pay attention to following:

  • Each model class must be annotated with com.github.jasminb.jsonapi.annotations.Type annotation
  • Each class must contain an String attribute annotated with com.github.jasminb.jsonapi.annotations.Id annotation
  • All relationships must be annotated with com.github.jasminb.jsonapi.annotations.Relationship annotation
Type annotation

Type annotation is used to instruct the serialisation/deserialisation library on how to process the given model class. Annotation has single property value which is required and it should be set to to whatever is the designated JSON API SPEC name for that type.

Example:

@Type("book")
public class Book {
 ...
}
Id annotation

Id annotation is used to flag an attribute of a class as an id attribute. Each resource class must have an id field and it must be of type String (defined by the JSON API specification).

Id is a special attribute that is, together with type, used to uniquely identify an resource.

Id annotation has no attributes.

Example:

@Type("book")
public class Book {
  
  @Id
  private String isbn;
  ...
}
Relationship annotation

Relationship annotation is used to designate other resource types as a relationships.

Imagine modeling a simple library application, you would end up having a Book resource and another logical resource would be Author.

You can model this as two different classes where Book resource would have an relationship to an Author:

@Type("book")
public class Book {
  @Id
  private String isbn;
  private String title;
  
  @Relationship("author")
  private Author author;
}

Relationship annotation has following attributes:

  • value
  • resolve
  • serialise

Value attribute is required and each relationship must have it set (value attribute represents the 'name' of the relationship).

Resolve attribute is used to instruct the library on how to handle server responses where resource relationships are not provided in included section but are rather returned as type and id combination.

Library has a support for registering global and typed relationship resloves which are used to resolve unresolved relationships. Resolving a relationship means using provided links attribute to perform additional HTTP request and get the related object using the link provided.

Relationship resolver interface has a single method:

byte [] resolve(String relationshipURL);

After implementing relationship resolver, in order to use it, one must register it with the instance of the ResourceConverter.

Example:

ResourceConverter converter = new ResourceConverter(Book.class, Author.class);
converter.setGlobalResolver(new CustomRelationshipResolverInstance());

Besides support for global resolvers, there is an option to have different resolvers for different resource types:

ResourceConverter converter = new ResourceConverter(Book.class, Author.class);
converter.setTypeResolver(new CustomBooksResolver(), Book.class);
converter.setTypeResolver(new CustomAuthorResolver(), Author.class);

Serialise attribute is used to instruct the serialisar whether to include or exclude given relationship when serialising resources. I is enabled by default, if disabled relationship will not be serialised.

Full example

Define simple POJO, please pay attention to added annotations:

@Type("book")
public class Book {
  
  @Id
  private String isbn;
  private String title;
  
  @Relationship("author")
  private Author author;
  
  # getters and setters
}

@Type("author")
public class Author {
  
  @Id
  private String id;
  private String name;
  
  @Relationship("books")
  private List<Book> books;
  
  # getters and setters
}

Create a converter instance:

ResourceConverter converter = new ResourceConverter(Book.class, Author.class);

// To convert raw data into POJO
byte [] rawResponse = ...get data from wire
Book book = converter.readObject(rawResponse, Book.class);

// To convert book object back to bytes
byte [] rawData = converter.writeObject(book);

Note that calling readObject(...) or readObjectCollection(...) using content that contains errors ({"errors" : [{...}]}) attribute will produce ResourceParseException.

Thrown exception has a method (getErrorResponse()) that returns parsed errors content. Errors content is expected to comply to JSON API Spec.

Example usage with retrofit

As as first step, define your model classes and annotate them using annotations described above.

After defining models, define your service interfaces as you would usually do with 'standard' JSON/XML APIs.

To create retrofit instance:

// Create object mapper
ObjectMapper objectMapper = new ObjectMapper();

// Set serialisation/deserialisation options if needed (property naming strategy, etc...)

Retrofit retrofit = new Retrofit.Builder()
		.baseUrl("https://yourapi")
		.addConverterFactory(new JSONAPIConverterFactory(objectMapper, Book.class, Author.class))
		.build();
		
// Create service using service interface

MyBooksService<Book> booksService = retrofit.create(MyBooksService.class);

Synchronous usage
Response<Book> bookResponse = booksService.find("123").execute();

if (bookResponse.isSuccess()) {
    // Consume response
} else {
    ErrorResponse errorResponse = ErrorUtils.parseErrorResponse(bookResponse.errorBody());
    // Handle error
}
Asynchronous usage
Call<Book> bookServiceCall = service.getExampleResource();

bookServiceCall.enqueue(new Callback<Book>() {
  @Override
  public void onResponse(Response<Book> bookResponse, Retrofit retrofit) {
    if (bookResponse.isSuccess()) {
        // Consume response
    } else {
        ErrorResponse errorResponse = ErrorUtils.parseErrorResponse(bookResponse.errorBody());
        // Handle error
    }
  }
  
  @Override
  public void onFailure(Throwable throwable) {
    // Handle network errors/unexpected errors
  }
});

About

The JSON-API converter uses Retrofit and Jackson to deserialize JSON-API compliant documents to Java objects. Forked from https://github.com/jasminb/jsonapi-converter

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages