Giter Site home page Giter Site logo

jsonapi-converter's Introduction

jsonapi-converter

Donate

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

Maven:

<dependency>
  <groupId>com.github.jasminb</groupId>
  <artifactId>jsonapi-converter</artifactId>
  <version>0.13</version>
</dependency>

Gradle:

implementation 'com.github.jasminb:jsonapi-converter:0.13'

SBT:

libraryDependencies += "com.github.jasminb" % "jsonapi-converter" % "0.13"

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.14-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
  • Attributes in the API that are not well-formed Java identifiers, must use JsonProperty 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 whatever is the designated JSON API SPEC name for that type.

Example:

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

Note that @Type annotation is not inherited from supperclasses.

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.

In case field annotated by the @Id annotation is not a String field, @Id annotation needs to be configured with proper ResourceIdHandler. Lirary provides handlers for Long and Integer types, in case types other than those mentioned are used, user must implement and provide proper id handler.

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

Id annotation is inheritable, one can define a base model class that contains a field with @Id annotation and then extend it to create a new type.

Example:

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

Example with inheritance:

public class BaseModel {
  @Id
  private String id;
}

@Type("book")
public class Book extends BaseModel {
  # Your custom member variables
}

Example using Long as id

@Type("book")
public class Book {
  
  @Id(LongIdHandler.class)
  private Long id;
  ...
}

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 a 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
  • relType

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 resolves 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.

Relationship type (relType) is used to instruct the library on how to resolve link data from raw server responses in order to resolve given relationship.

There two different relationship types:

  • SELF (self link will be followed to resolve relationship
  • RELATED (related link will be followed)

Have in mind that relationship (same as id) is inheritable and can be defined in a base class.

Polymorphic Relationships

In order to support polymorphic relationships, an interface needs to be created and than implemented by all possible types relationship supports. Created interface is used as a relationship's type (see example below).

Example response containing multiple types in a relationship:

{
  "data": {
    "type": "dealerships",
    "id": "1",
    "attributes": {
      "name": "Dealership name"
    },
    "links": {
      "self": "http://example.com/dealerships/1"
    },
    "relationships": {
      "inventory": {
        "links": {
          "self": "http://example.com/dealerships/1/relationships/inventory",
          "related": "http://example.com/dealerships/1/inventory"
        },
        "data": [
          { "type": "cars", "id": "2" },
          { "type": "trucks", "id": "1" }
        ]
      }
    }
  },
  "included": [{
    "type": "cars",
    "id": "2",
    "attributes": {
      "make": "BMW",
      "model": "i8 Roadster"
    },
    "links": {
      "self": "http://example.com/cars/2"
    }
  }, {
    "type": "trucks",
    "id": "1",
    "attributes": {
      "make": "Ford",
      "model": "Semi"
    },
    "links": {
      "self": "http://example.com/trucks/1"
    }
  }]
}

Needed classes, and example usage:

public interface Driveable {}

@Type("cars")
public class Car implements Driveable {
	@Id
	private String id;
	private String model;
	private String make;
	// Getters and setters...
}

@Type("trucks")
public class Truck implements Driveable {

	@Id
	private String id;
	private String make;
	private String model;
        // Getters and setters...
}

@Type("dealerships")
public class Dealership {
	@Id
	private String id;
	private String name;
	private String city;

        // Interface is used as relalationship type (instead of concrete resource type)
	@Relationship("inventory")
	private Collection<Driveable> automobiles;
}

// Putting everything together
ResourceConverter converter = new ResourceConverter("https://api.example.com", Car.class, Dealership.class, Truck.class);
JSONAPIDocument<Dealership> document = converter.readDocument(apiResponse, Dealership.class);

Relationship meta and links

jsonapi-spec allows for having relationship-level metadata and links.

In order to gain access to returned relationship meta and links or ability to serialize it, use following annotations:

  • RelationshipMeta
  • RelationshipLinks

Here is a version of the Book class with relationship meta/links added:

@Type("book")
public class Book {
 @Id
 private String isbn;
 private String title;
 
 @Relationship("author")
 private Author author;
 
 @RelationshipMeta("author")
 private Meta authorMeta
 
 @RelationshipLinks("author")
 private Links authorLinks
}

Make sure not to confuse relationship meta and links with regular meta-data and link data explained below.

Meta annotation

By JSON API specification, each resource can hold meta attribute. Meta can be arbitrary object that is defined by the API implementation.

In order to map and make meta available trough resource conversion, one must create a model that coresponds to the meta object returned by the API, create a member variable in the resource class using created model and annotate it using the @Meta annotation.

Meta example:

# Meta model class

public class MyCustomMetaClass {
    private String myAttribute;
    
    public String getMyAttribute() {
    	return myAttribute;
    }
    
    public void setMyAttribute(String value) {
    	this.myAttribute = value;
    }
}

# Resource class with meta attribute

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

Meta annotation/attriubutes are inheritable.

Links annotation

JSON API specification allows for links to be part of resources. Links usually cary information about the resource itself (eg. its URI on the server).

Liks are not arbitray objects, JSON API spec provides links structure therefore it is not required to create a new model to make links object available.

Library provides a com.github.jasminb.jsonapi.Links class that must be used in order to make links data available in resources.

Example:

@Type("book")
public class Book {
  @Id
  private String isbn;
  private String title;
  
  @Relationship("author")
  private Author author;
  
  @Meta
  private MyCustomMetaClass meta;
  
  @Links
  private com.github.jasminb.jsonapi.Links links;
}

Links are inheritable.

Attribute annotations

If your JSON API endpoint returns attributes that do not map well to Java identifiers, you'll get a fatal error on deserialization. The log message will tell you about an unrecognized field with that name. To fix it, you can use com.fasterxml.jackson.annotation.JsonProperty.

Example: your JSON API endpoint returns something like this

{
    "data": [
        {
            "id": "1",
            "type": "gears",
            "attributes": {
                "tooth-count": 13,
                "tooth-depth": 21
            }
        }
    ]
}

then your model must be:

import com.fasterxml.jackson.annotation.JsonProperty;

@Type("gears")
public class Gear {
  @Id
  private String id;

  @JsonProperty("tooth-count")
  private Long toothCount;
  @JsonProperty("tooth-depth")
  private Long toothDepth;
}

This also lets you use different names for your fields than your API.

Full example

Define simple POJO, please pay attention to the added annotations:

# Meta is optional, one does not have to define or use it
public class Meta {
    private String myAttribute;
    
    public String getMyAttribute() {
    	return myAttribute;
    }
    
    public void setMyAttribute(String value) {
    	this.myAttribute = value;
    }
}

# Creating base class is optional but allows for writing more compact model classes
public class BaseResource {
    @Id
    private String id;
    
    @Meta
    private Meta meta;
    
    @Links
    private Links links;
}

@Type("book")
public class Book extends BaseResource {
  private String title;
  
  @Relationship("author")
  private Author author;
  
  @RelationshipMeta("author")
  private Meta authorMeta
  
  @RelationshipLinks("author")
  private Links authorLinks
  
  # getters and setters
}

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

Create a converter instance:

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

// Get response data
byte [] rawResponse = ...get data from the wire

// To convert raw data into single POJO
JSONAPIDocument<Book> bookDocument = converter.readDocument(rawResponse, Book.class);
Book book = bookDocument.get();

// To convert raw data into collection
JSONAPIDocument<List<Book>> bookDocumentCollection = converter.readDocumentCollection(rawResponse, Book.class);
List<Book> bookCollection = bookDocumentCollection.get();

Note that calling readDocument(...) or readDocumentCollection(...) 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.

Top level links and meta

Besides having links and meta information on resource level, by JSON API spec it is also possible to have meta, links or both as top level objects in server responses.

To gain access to top level meta/links, this library provides convenience methods available in JSONAPIDocument, namely:

  • getMeta()
  • getLinks()

Resource serialization

Besides providing options to deserialize json-api spec complaint resource representation, library also includes support for serializing resources.

Following are available serialization options that can be enabled/disabled on ResourceConverter instance:

  • INCLUDE_META enabled by default, if enabled, meta data will be serialized
  • INCLUDE_LINKS enabled by default, if enabled links will be serialized
  • INCLUDE_RELATIONSHIP_ATTRIBUTES disabled by default, if enabled, relationship objects will be serialized fully, this means that besides generating relationship objects for each relationship, included section will be created that contains actual relationship attributes

To enable or disable serialization options:

ResourceConverter converter = ...
# Enable generating included section
converter.enableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES);

# Disable generating included section
converter.disableSerializationOption(SerializationFeature.INCLUDE_RELATIONSHIP_ATTRIBUTES);

Example with INCLUDE_RELATIONSHIP_ATTRIBUTES disabled:

{
  "data": {
    "type": "articles",
    "id": "id",
    "attributes": {
      "title": "title"
    },
    "relationships": {
      "author": {
        "data": {
          "type": "people",
          "id": "id"
        }
      }
    }
  }
}

Example with INCLUDE_RELATIONSHIP_ATTRIBUTES enabled:

{
  "data": {
    "type": "articles",
    "id": "id",
    "attributes": {
      "title": "title"
    },
    "relationships": {
      "author": {
        "data": {
          "type": "people",
          "id": "id"
        }
      }
    }
  },
  "included": [
    {
      "type": "people",
      "id": "id",
      "attributes": {
        "firstName": "John"
      }
    }
  ]
}

Example usage with retrofit

As a 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 booksService = retrofit.create(MyBooksService.class);
Synchronous usage
Response<JSONAPIDocument<Book>> bookResponse = booksService.find("123").execute();

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

bookServiceCall.enqueue(new Callback<Book>() {
  @Override
  public void onResponse(Response<JSONAPIDocument<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
  }
});

Notice that expected return types in MyBookService calls are all wrapped with JSONAPIDocument, this is intended way to use the library since it allows for gaining access to response level meta and links data.

Example service interface:

public interface MyBooksService {
    @GET("books")
    Call<JSONAPIDocument<List<Book>> allBooks();
}

Tips

If you need a String as an output when serializing objects, you can do the following:

byte [] serializedObject = resourceConverter.writeObject(...);
String serializedAsString = new String(serializedObject);

Note for kotlin users

Have in mind that using open classes as type parameters in relationship collections will not work, for instance:

@Type("base")
open class MyClass {

    @Relationship("my-relationship")
    var bases: List<MyClass>? = null
}

Removing the open modifier will solve the issue.

Note for Proguard

This library use reflection at runtime for checking if all registred types have an ID-field.
You have to add this proguard rule:

# Keep jsonapi-converter relative fields
-keepclassmembers class * {
    @com.github.jasminb.jsonapi.annotations.Id <fields>;
}

Proguard should remove all fields and methods that you are not accessing. If you would like to keep all deserialized fields, you can add a rule for unconditionally keep all your POJO classes.

# Keep all POJO classes
-keep class com.example.json.** {*;}

Replace com.example.json by the correct package.

# Keep custom id handlers
-keep class * implements com.github.jasminb.jsonapi.ResourceIdHandler

jsonapi-converter's People

Contributors

acroacm avatar bradroid avatar ddbullfrog avatar dependabot[bot] avatar emetsger avatar fabricehong avatar human avatar jasminb avatar jenny-messer avatar jordanmueller avatar jsteinbrunner avatar k3rni avatar kevinrob avatar marcocapoferri avatar masquerade0097 avatar naufalfachrian avatar peavers avatar prawana-perera avatar shyamalprasad avatar stefma avatar tklovett avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonapi-converter's Issues

Annotated @Links object is not serializing correctly

I've defined an entity that is annotating and adding a property annotated Links object, and when serializing, the output has:

  1. The links inside the attributes{} object, instead of at the top level, as shown in the spec: http://jsonapi.org

  2. It's duplicating the links object inside there, incorrectly.

  3. The links object is serializing a meta object, incorrectly.

Code:
`

import com.github.jasminb.jsonapi.JSONAPISpecConstants;
import com.github.jasminb.jsonapi.Link;
import com.github.jasminb.jsonapi.ResourceConverter;
import com.github.jasminb.jsonapi.annotations.Id;
import com.github.jasminb.jsonapi.annotations.Links;
import com.github.jasminb.jsonapi.annotations.Type;

import java.util.HashMap;
import java.util.Map;

@Type("dragon")
public class EntityWithLinks {

@Id
public String id = "TROGDOR-ABC";

@Links
public com.github.jasminb.jsonapi.Links links;

public String otherInfo = "TROGDOR COMES IN THE NIGHT";

public String getOtherInfo() {
    return otherInfo;
}

public void setOtherInfo(String otherInfo) {
    this.otherInfo = otherInfo;
}

public EntityWithLinks(){
    Map<String, Link> linksMap = new HashMap<>();
    linksMap.put(JSONAPISpecConstants.SELF, new Link("http://www.homestarrunner.com/sbemail58.html"));
    links = new com.github.jasminb.jsonapi.Links(linksMap);
}
public String getId() {
    return id;
}
public void setId(String id) {
    this.id = id;
}
public static void main(String[] args) throws Exception{
    ResourceConverter converter = new ResourceConverter(EntityWithLinks.class);
    byte[] bytes = converter.writeObject(new EntityWithLinks());
    System.out.println(new String(bytes));
}


}

`

and the output (made pretty)

{
    "data": {
        "type": "dragon",
        "id": "TROGDOR-ABC",
        "attributes": {
            "links": {
                "links": {
                    "self": {
                        "href": "http://www.homestarrunner.com/sbemail58.html",
                        "meta": {
                        }
                    }
                },
                "self": {
                    "href": "http://www.homestarrunner.com/sbemail58.html",
                    "meta": {
                    }
                }
            },
            "otherInfo": "TROGDOR COMES IN THE NIGHT"
        }
    }
}

is "included" required?

I'm having issues trying to read in a basic object.

It looks like a null pointer is being thrown inside the readObject right at

T result = readObject(dataNode, clazz, included);

Due to included being null...

Using a very simple demo app, and posting the follow request

{
  "data": {
    "type": "server",
    "attributes": {
      "nameTag": "sample project"
    }
  }
}

As include isn't a requirement according to the spec, is there something else I'm doing wrong here?

Included is not available for related resources while serializing.

Hi,
I tried to serialize 2 related resources with the 0.3 version, but the relation is only available with the type and id. The attributes of the relation which will be available in included is not getting displayed.

Actual Result,
{ "data": { "type": "mainResponse", "id": "3", "attributes": { "emp-id": "456879", "name": "John", "sub-response": { "id": "2", "ph-no": "1234569778", "vnet": "156478" }, }, "relationships": { "subResponse": { "data": { "type": "subResponse", "id": "2" } } } } }

POJO classes,
MainResponse.txt
SubResponse.txt

Pls give latest set of jars list to compile jsonapi convertor base, I am not using maven,i tried with following jar , but getting exceptions while executing an file

I am using the below listed jars

jackson-annotations-2.0.1.jar
jackson-core-2.5.0.jar
jackson-databind-2.7.1.jar
jsonapi-converter-0.3.jar
junit-4.12.jar
mockwebserver-3.0.0.jar
okhttp-1.2.0.jar
okhttp-2.7.5.jar
okhttp-3.2.0.jar
retrofit-2.0.1.jar

I am getting following exception trace

java -cp .:lib/*: com/github/jasminb/jsonapi/ResourceConverterTest
Exception in thread "main" java.lang.NoClassDefFoundError: com/fasterxml/jackson/annotation/JsonInclude$Value
at com.fasterxml.jackson.databind.cfg.MapperConfig.(MapperConfig.java:45)
at com.fasterxml.jackson.databind.ObjectMapper.(ObjectMapper.java:535)
at com.fasterxml.jackson.databind.ObjectMapper.(ObjectMapper.java:452)
at com.github.jasminb.jsonapi.ResourceConverter.(ResourceConverter.java:65)
at com.github.jasminb.jsonapi.ResourceConverter.(ResourceConverter.java:50)
at com.github.jasminb.jsonapi.ResourceConverterTest.main(ResourceConverterTest.java:33)
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.annotation.JsonInclude$Value
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)

Pls help me to fix the problem.

Bulk resource creation

I was trying to create multiple resources in one go, but failed to do so. Following test case with attached data failed (data where there is not "ID" provided for resources).

        converter.disableDeserializationOption(DeserializationFeature.REQUIRE_RESOURCE_ID);
        String rawData = IOUtils.getResourceAsString("users-no-id.json");
        List<User> users = converter.readDocumentCollection(rawData.getBytes(StandardCharsets.UTF_8), User.class).get();
        Assert.assertNotNull(users);
        Assert.assertEquals(2, users.size());
        Assert.assertEquals("liz", users.get(0).getName());
        Assert.assertEquals("john", users.get(1).getName());

ResourceConverter's createIdentifier(...) returns same identification (which is actually type of the resource) for every resources; thus ResourceCache provides same resource again.

users-no-id.txt

gradle dependency for snapshot not working

Error:Could not find com.github.jasminb:jsonapi-converter:0.4-SNAPSHOT.
Required by:
    :app:unspecified
<a href="searchInBuildFiles">Search in build.gradle files</a>

build.gradle

repositories {
        mavenLocal()
        mavenCentral()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/"}
    }

dependencies{
 compile 'com.github.jasminb:jsonapi-converter:0.4-SNAPSHOT'
}

ResourceConverter should allow deserializing arbitrary JSON-API–compliant objects

Hi,

To my knowledge, the only way to deserialize a JSON-API object with a ResourceConverter at the moment is to use the readDocument(byte[], Class). This requires prior knowledge of the type of object to be deserialized - this can sometimes be an issue, as it is not always possible to know what type of object we are expecting. For example, my Android client is registered to receive push notifications, which can be one of several types. Given that @Type-annotated models exist to deserialize each of these types (and are registered with the ResourceConverter), it would be nice to be able to use the ResourceConverter to determine which model the object should be deserialized into rather than having to work it out manually in advance.

Thoughts?

Deserealize Links at top level

Hi,

We are using the 0.4 version of the library and also tried in the 0.5 snapshot. We tried to deserealize a collection with the Links object at top level:

{ "data": [ ... ], "meta": { "totalResults": "2709" }, "links": { "first": "first_link", "next": "next_link", "last": "last_link" } }

We tried to add in our resource class the @links annotation but it's always null, I think that's because our Links object is defined at top level right?

How can we parse the Links object?

Thanks for your help

Maven package

Hello,
nice work. It'd be great to have this package pushed on maven central repository 👍

Add support for attribute rename

@Attribute(name = "account_id")
private String acid;

This should generate json

"attributes": {
    "account_id": "123"
}

instead of

"attributes": {
    "acid": "123"
}

Links are not working for relationships

I tried with 0.3 version to Marshal the resources in the below json response

{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
}
}

However I am not able to generate "links" object with self and related members. Is that not available in 0.3. Is there any plan to include them in next version

JAX-RS Compatibility

Does Json api supports JAX-RS message readers and writers. I am trying to convert raw response to Rest resources for a JAX-RS compliant restful resource. Below are the success and failure scenarios.

Success Scenario :-
ResourceConverter converter = new ResourceConverter(User.class);
User.class will be read dynamically from JAX-RS MessageBodyReader and I am able to conver raw response to java resource

Failure Scenarion :
ResourceConverter converter = new ResourceConverter(User.class,Status.class);
Through jax-rs messagebodyreader it is difficult to read multiple object types (which are related each other).

Hence trying to understand json api converter is compatible with jax-rs based frameworks.

Potential Problem in Parsing Included Resources

` private Map<String, Object> parseIncluded(JsonNode parent)
throws IOException, IllegalAccessException, InstantiationException {
Map<String, Object> result = new HashMap<>();

    if (parent.has(INCLUDED)) {
        // Get resources
        List<Resource> includedResources = getIncludedResources(parent);

        if (!includedResources.isEmpty()) {
            // Add to result
            for (Resource includedResource : includedResources) {
                result.put(includedResource.getIdentifier(), includedResource.getObject());
            }

            ArrayNode includedArray = (ArrayNode) parent.get(INCLUDED);
            for (int i = 0; i < includedResources.size(); i++) {
                Resource resource = includedResources.get(i);

                // Handle relationships
                JsonNode node = includedArray.get(i);
                handleRelationships(node, resource.getObject());
            }
        }
    }

    return result;
}`

The method getIncludedResources(parent) returns only the resources that their types is registered in configuration. In case that included part of json has a resource that is not registered or just ignored the includedArray nodes are not in the same order of resources in includedResources and their size are different and handleRelationships(node, resource.getObject()) in for block gets wrong data to handle!

Meta attributes for resources in data collections

First off, this is a super helpful library, thanks so much for the work you've put in so far.

At the moment the meta field on data objects is not supported as part of the JSON deserialization. Issue #15 discusses the missing meta field on the collection itself, but this does not address the lack of meta attributes on the individual resources within a collection, such as the following JSON:

{
  "data": [
    {
      "id": "1",
      "type": "check_ins",
      "attributes": {
        ...
      },
      "relationships": {
        ...
      },
      "links": {
        ...
      },
      "meta": {
        "is_me": false,
        "is_friend": false,
        "has_liked": true,
        "has_commented": false,
        "comments_count": 1,
        "likes_count": 2
      }
    },
    ...
  ],
  "included": [
    ...
  ],
  "links": {
    ...
  }
}

As a temporary workaround, I have checked out @emetsger's develop-wip branch. That PR goes most of the way to solving this issue, except that the call to readObjectInternal does not parse the meta field. To address this, I copied the following block of code:

// handling of meta node
if (source.has(META)) {
    Field field = META_FIELD.get(clazz);
    if (field != null) {
        Class<?> metaType = META_TYPE_MAP.get(clazz);
        Object metaObject = objectMapper.treeToValue(source.get(META), metaType);
        field.set(result, metaObject);
    }
}

... from the method readObjectInternal(byte [] Class<T>, ResolverState) to readObjectInternal(JsonNode, Class<T>, Map<String, Object>, ResolverState).

Let me know if you have any questions!

Question. How to parse meta data?

Hello guys. How to read that json meta data?
I removed unnecessary json data.

{
  "data": [
    {
      "id": "224",
      "type": "job_offers",
      "attributes": {
        "opens_at": "2016-07-18T17:42:02.935+01:00",
        "closes_at": "2016-09-16T17:42:02.935+01:00",
        "experience_level_id": 2,
        "experience_value_min_id": 1,
        "experience_value_max_id": 4,
        "salary_min": 70000,
        "salary_max": 90000,
        "application_status": "created",
        "vertical_id": 1
      },
      "relationships": {
        "office": {
          "data": {
            "id": "17",
            "type": "offices"
          }
        }
      }
    }
  ],
  "included": [

  ],
  "links": {},
  "meta": {
    "application_statuses": {
      "created": "text1",
      "for_screening": "text2",
      "pending": "text3",
      "shortlisted": "text4",
      "rejected": "text5",
      "hired": "text6"
    }
  }
}

collect annotated fields also from super classes

what would greatly improve the library's flexibility to be able to define common annotated fields in a superclass.

usefull for ids (and meta if available)

something like that in ReflectionUtils:

public static List<Field> getInheritedAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotation) {
        List<Field> fields = new ArrayList<>();
        Class<?> currentClass = clazz;
        while(currentClass!=null) {
            fields.addAll(getAnnotatedFields(currentClass, annotation));
            currentClass = currentClass.getSuperclass();
        }
        return fields;
    }
}

I would like to get the relationships- authors-links-self, donno how to get it , Pls help me to get the self link

{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever.",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
}
},
"included":
{
"type": "people",
"id": "42",
"attributes": {
"name": "John",
"age": 80,
"gender": "male"
}
}
}

inheritable relationships

"Have in mind that relationship (same as id) is inheritable and can be defined in a base class."

Hello, can you help me do this, please?

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type",
        visible = true,
        defaultImpl = Vehicle.class
)

@JsonSubTypes({
        @JsonSubTypes.Type(value = Car.class, name = "CAR"),
        @JsonSubTypes.Type(value = Bike.class, name = "BIKE")
})


@Type("vehicle")
public class Vehicle {

    @Id
    public String id;

    @Relationship("address")
    protected Address address;

}

@Type("vehicle")
public class Car extends Vehicle {

    @Id
    public String id;

    @Relationship("address")
    protected Address address;

}

If i remove @id or @relationship from the children, they are not inherited from the parent and returns an error "All resource classes must have a field annotated with the @id annotation" or, in the case of the Relationships, just returns null.
Even if when change @type to car.

Thank you

Can not create parent and child objects in one request

This may be part of the JSONAPI spec but I couldn't find a definitive answer in it. We are using the library on both the client and server. The client sends a request to create a new resource. This resource contains child resources that also need to be created with it. None of the objects have IDs.

{
  "data": {
    "type": "parents",
    "attributes": {
      "name": "Parent Name"
    },
    "relationships": {
      "children": {
        "data": [
          {
            "type": "children",
            "attributes": {
              "name": "Child 1"
            }
          },
          {
            "type": "children",
            "attributes": {
            "name": "Child 2"
            }
          }
        ]
      }
    }
  }
}

On the server the converter ignores the children since there is no ID in them. The final parent object has no children once the document is converted. This is because ValidationUtils.isRelationshipParsable defines a valid relationship to require an ID. Is this part of the spec? Could this be a deserialization option? I would be happy to provide PR to change once I understand the spec better around this issue.

relationship of included data is null

Hi

the following example is not parsed correctly :

{
  ...

"included": [
    {
      "id": "1",
      "type": "office",
      "relationships": {
    "responsible_person": {
      "data": {
        "id": "10",
        "type": "responsible_person"
      }
    }
      },
      "attributes": {
    ...
      }
    },
    {
      "id": "10",
      "type": "responsible_person",
      "attributes": {
    ...
      }
    }
]
}

the office.responsible_person relationship will be null.

While parsing the object 'office' in method ResourceConverter.parseIncluded, it will call readObject to create office object with '(Map)null' as cache parameter. Having a null cache, 'handleRelationships' method won't be called to fill the object's relationships. even it a cache would be properly passed, the 'responsible_person' is not yet parsed in the while loop of ResourceConverter.parseIncluded.

It should be done in two phases I guess. One for creating objects, and one for filling them.

Can you confirm relationships in included data are not handled by the library ?

thanks !

Eager vs Lazy resolution of relationships

The current behavior of the JSON-API Converter when resolving relationships is to resolve them eagerly, and recursively.

If you have a model that represents a hierarchy of objects that is arbitrarily deep, resolving a single object in that hierarchy will result in recursivly resolving all of the nodes below it.

For example, retrieving an instance of Node, below, may result in traversing a hierarchy that is arbitrarily deep:

Example Java

@Types("node")
class Node {

  @Id
  String id;

  @Relationship(value = "children", resolve = true, relType = RelType.RELATED)
  List<Node> children;

  // ...
}

Example JSON

{
  "data": [
    {
      "type": "node",
      "id": "1",
      "relationships": {
        "children": {
          "links": {
            "related": "http://example.com/node/1/children"
          }
        }
      }
    }
  ]
}

This behavior may not be desirable for many reasons, least of which have to do with the performance of retrieving deeply nested structures.

There may be multiple ways to address this. For example:

  • Add a depth attribute to the @Relationship annotation, which controls how many levels deep the resolution will go
    • Let n be value of the depth attribute; the problem is you have to gracefully handle the situation when the developer wants to resolve the object that is n+1 levels deep.
  • Update the behavior of the resolution mechanism to lazily resolve relationships
    • This doesn't seem to be within the scope of the JSON API Converter. Once a JSON document is converted into POJOs, the converter has no interaction with the POJO model, and wouldn't be able to lazily resolve relationships.
  • Instead of resolving the URL and deserializing the response document to a Java object, simply save the URL as a string in the Java model. This is essentially lazy loading, which puts the burden of resolving and deserializing a reference explicitly on the developer of the model.

Link resolution

This is a question more than an issue-

ResourceConverter.handleRelationships(...) only considers the self link when attempting resolution. However, I am working with some JSON relationships that look like:

"relationships": {
            "files": {
                "links": {
                    "related": {
                        "href": "http://osf:8000/v2/nodes/mrusa/files/",
                        "meta": {}
                    }
                }
            }

When handleRelationships(...) encounters this JSON, it will not attempt resolution, because there is no links member named self, only a member named related (note that this JSON complies with the spec, which requires one of a links, data, or meta object).

Per the JSON-API 1.0 spec it seems that the links object for a Relationship can contain both self and related links, where

  • a GET on a self link returns the relationship link object
  • a GET on a related link returns the resource objects

So, what should the behavior of ResourceConverter.handleRelationships(...) be when it encounters a Relationship object with a links member that has no self but has a related member?

Custom Object inside attributes is not parsed, it only allows String or List<String>

Hello,

I have an custom object inside attributes, which is not a relationship, that i can't parse.
If the variable is not a String or a List<>String>, is not parsed and returns null.
I give you an example:

JSON:

{ data: { id: 1, type: countries, attributes: { name: "France", capital: "Paris", location: { latitude: "46.1355292", longitude: "-2.2776085" } } } }

JAVA:

`@type("countries")
public class Country {

@id
public String id;

public String name;

public String capital;

public Location location;

public static class Location {

    public String latitude;

    public String longitude;

    public Location() {}
}

public Country() {}

}`

Thank you

Pagination of primary data not supported

It seems pagination of primary data is not supported; this is because readObjectCollection returns List<T> - the return type is not able to expose pagination links (or meta information, for that matter).

The JSON below represents paginated primary data. The total size of the primary data is m but only n results are returned:

{
    "data": [
        {
          // object 1
        },
        { 
          // object 2
        },
        {
          // object n
        }
    ],
    "links": {
        "first": null,
        "last": "http://example.com/nodes/?page=m",
        "prev": null,
        "next": "http://example.com/nodes/?page=2",
    }
}

The ResourceConverter will represent the first page of results in a List<T> with a size() of n. The client has no way of knowing if the results are paginated or not, because List doesn't expose any pagination state.

Per the spec, pagination MAY be supported, and if so, MUST be represented by a top-level links object:

Pagination

A server MAY choose to limit the number of resources returned in a response to a subset (“page”) of the whole set available.

A server MAY provide links to traverse a paginated data set (“pagination links”).

Pagination links MUST appear in the links object that corresponds to a collection. To paginate the primary data, supply pagination links in the top-level links object. To paginate an included collection returned in a compound document, supply pagination links in the corresponding links object.

The following keys MUST be used for pagination links:

  * first: the first page of data
  * last: the last page of data
  * prev: the previous page of data
  * next: the next page of data

Keys MUST either be omitted or have a null value to indicate that a particular link is unavailable.

com.github.jasminb.jsonapi.Links needs to override Equals()

When writing a test, if I serialize and deserialize an object with links, a new Links() object is created, and since there's no equals() method, they're not considered equal.

Please include a valid equals() and hashCode() method, preferrably using EqualsBuilder or similar semantics!

Thanks!

NullPointerException while parsing jsonapi object

hi

my json object look like :

{
    "data" : {
        "id" : "1"
        ...
    }
    ...
}

I have a NullPointerException in ResourceConverter class. in method "setIdValue", idValue is null at line : "idField.set(target, idValue.asText());"

Debuging the method readObjectCollection in ResourceConverter, I saw that while looping over the properties of the data element,
the readObject method used here expect the source ton contain a json container with an 'id' key.

the type of the returned object from e.get("data") is "ObjectNode"

With this object type, the returned element from objectNode.iterator() from jackson-databind-2.7.1, is the list of values (ex: "1"), not the expected list of object (ex : "id"=>"1").

code of readObjectCollection method:

public <T> List<T> readObjectCollection(byte[] data, Class<T> clazz) {
        try {
            JsonNode e = this.objectMapper.readTree(data);
            Map included = this.parseIncluded(e);
            ArrayList result = new ArrayList();
            Iterator i$ = e.get("data").iterator();
            while(i$.hasNext()) {
                JsonNode element = (JsonNode)i$.next();
                Object pojo = this.readObject(element, clazz, included);
                result.add(pojo);
            }
            return result;
        } catch (Exception var9) {
            throw new RuntimeException(var9);
        }
    }

StackOverflow error if cyclical graph

JSON API allows me to have cyclical references, ie:
A teacher that has a relationship to a student, and that student having a relationship to the teacher.

However, this requires that the serialization and deserialization map ID's such that we can only create one reference, as walking the references will result in a cycle.

The below example shows this. We could create a type/id map during serialization so that we know if we've already started serializing an object, and then just no-op the relationship serialization call.


import com.github.jasminb.jsonapi.JSONAPIDocument;
import com.github.jasminb.jsonapi.ResourceConverter;
import com.github.jasminb.jsonapi.annotations.Id;
import com.github.jasminb.jsonapi.annotations.Relationship;
import com.github.jasminb.jsonapi.annotations.Type;
import com.sun.deploy.model.Resource;

import java.util.Arrays;
import java.util.List;

public class RecursiveOverflow {

    public static void main(String[] args) throws Exception{
        Teacher mathTeacher = new Teacher();
        mathTeacher.setTeacherName("Snake Plissken");
        mathTeacher.setTeacherId("plsk-123");
        StudentInClass studentInClass = new StudentInClass();
        studentInClass.setStudentId("student-jones-348");
        studentInClass.setMyMathTeacher(mathTeacher);
        mathTeacher.setStudentList(Arrays.asList(studentInClass));
        JSONAPIDocument<Teacher> teacherJSONAPIDocument = new JSONAPIDocument<>(mathTeacher);

        ResourceConverter converter = new ResourceConverter(Teacher.class, StudentInClass.class);
        byte[] bytes = converter.writeDocument(teacherJSONAPIDocument);
        String jsonOutput = new String(bytes);
        System.out.println("jsonOutput");
    }

    @Type("teacher")
    public static class Teacher{

        @Id
        String teacherId;
        String teacherName;
        @Relationship("student")
        List<StudentInClass> studentList;

        public String getTeacherId() {
            return teacherId;
        }

        public void setTeacherId(String teacherId) {
            this.teacherId = teacherId;
        }

        public String getTeacherName() {
            return teacherName;
        }

        public void setTeacherName(String teacherName) {
            this.teacherName = teacherName;
        }

        public List<StudentInClass> getStudentList() {
            return studentList;
        }

        public void setStudentList(List<StudentInClass> studentList) {
            this.studentList = studentList;
        }
    }

    @Type("student")
    public static class StudentInClass{
        @Id
        String studentId;
        @Relationship("teacher")
        Teacher myMathTeacher;

        public String getStudentId() {
            return studentId;
        }

        public void setStudentId(String studentId) {
            this.studentId = studentId;
        }

        public Teacher getMyMathTeacher() {
            return myMathTeacher;
        }

        public void setMyMathTeacher(Teacher myMathTeacher) {
            this.myMathTeacher = myMathTeacher;
        }
    }



}


Retrofit2 binding / dependency

Hi

We just bumped our retrofit version to the latest. The latest version is not retro-compatible (so they also changed the package names from retrofit to retrofit2).

Since jsonapi-converter is bound to the previous retrofit version, I had to recreate the retrofit dependant classes :

  • ErrorUtils
  • JSONAPIConverterFactory
  • JSONAPIRequestBodyConverter
  • JSONAPIResponseBodyConverter

If you wanna add this possibility, you probably want to design this as a separate binding library, to avoid jsonapi-converter to be dependent from both retrofit and retrofit2. So in case you would like to decouple jsonapi-converter from retrofit version and use what I did as a jsonapi-converter submodule, I put it on github :
https://github.com/team-rawbot/jsonapi-retrofit2-adapter

Feel free to integrate it.
This is a gradle project.

cheers

ResourceConverter.handleRelationships(...) does not support the object representation of a link

The JSON API spec allows for two representations of a link:

A link MUST be represented as either:

  • a string containing the link's URL.
  • an object ("link object") which can contain the following members:
    • href: a string containing the link's URL.
    • meta: a meta object containing non-standard meta-information about the link.

The current implementation of handleRelationships(...) only handles the first representation - the string representation. Links represented as objects are not supported.

Relationship parsing logic relies on optional resource linkage

Summary

In RelationshipResolver, the method isCollection(JsonNode) is used a priori to determine whether or not a relationship link will return an array or an object. This cannot be done a priori if resource linkages (optional per the JSON API spec) are absent from a relationship.

See this test, which fails on develop.

Example JSON

If the jsonapi-converter is parsing the JSON below, and attempts resolve the related relationship (http://example.com/articles/1/comments):

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    },
    "relationships": {
      "comments": {
        "links": {
          "related": "http://example.com/articles/1/comments"
        }
      }
    }
  }]
}

And the JSON returned by http://example.com/articles/1/comments is this:

{
  "data": [
    {
      "type": "comments",
      "id": "5",
      "attributes": {
        "body": "First!"
      }
    },
    {
      "type": "comments",
      "id": "12",
      "attributes": {
        "body": "I like XML better"
      }
    }
  ]
}

Parsing of the http://example.com/articles/1/comments response will fail. Note that there is no resource linkage in the initial JSON document (the one representing articles). Had the initial JSON document contained a resource linkage in the comments relationship like so:

"relationships": {
      "comments": {
        "links": {
          "related": "http://example.com/articles/1/comments"
        },
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    }

Parsing of the http://example.com/articles/1/comments document would succeed.

Because resource linkages are optional per the specification, the initial JSON document, sans resource linkage, should result in a successful response.

Discussion

isCollection(JsonNode) introspects on the relationship object and uses the resource linkage as hint (resource linkages for relationships must properly represent to-many relationships as arrays, and to-one relationships as objects).

However, resource linkages are not required to be present in a relationship:

A “relationship object” MUST contain at least one of the following:

links: a links object containing at least one of the following:
  * self: a link for the relationship itself (a “relationship link”). This link allows the client to directly manipulate the relationship. For example, removing an author through an article’s relationship URL would disconnect the person from the article without deleting the people resource itself. When fetched successfully, this link returns the linkage for the related resources as its primary data. (See Fetching Relationships.)
  * related: a related resource link
data: resource linkage
meta: a meta object that contains non-standard meta-information about the relationship.

So resource linkages, if present, may be used a priori to determine the to-many or to-one-ness of a relationship, but if the resource linkage is not present, then the parsing logic must be prepared to parse a collection or an object after dereferencing the relationship.

Documentation, with focus on a comprehensive example

looking at the response example at http://jsonapi.org/ .

is there a possibility to create documentation for your jsonapi-converter that mimics the response from the 'blog' example that has the top levels 'links' and 'data' .
today my entities have the following annotations

@XmlAccessorType(value = XmlAccessType.FIELD)
@XmlRootElement

and JPA-annotations such as

@id
@column(name = "UUID")

I would like to comply to the jsonapi.org-standard when serving JSON to my clients, this library would be great if I know the best practice.

-i

Retrofit cannot create converter for JSONAPIDocument<List<Model>>

Hi,

I have just updated to version 0.4-SNAPSHOT of the library, and I am trying to use the JSONAPIDocument capture the metadata of resource collections. However, this does not seem to work with Retrofit:

Caused by: java.lang.IllegalArgumentException: Unable to create converter for com.github.jasminb.jsonapi.JSONAPIDocument<java.util.List<com.my.package.Model>>
   for method CheckInsEndpoint.retrieveCheckInsCall
   at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:720)
   at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:706)
   at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:167)
   at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:166)
   at retrofit2.Retrofit$1.invoke(Retrofit.java:145)
   at java.lang.reflect.Proxy.invoke(Proxy.java:813)
   at $Proxy5.retrieveCheckInsCall(Unknown Source)
   at com.my.package.HomeFeedPresenter$1.call(HomeFeedPresenter.java:149)
   at com.my.package.HomeFeedPresenter$1.call(HomeFeedPresenter.java:146)
   at rx.internal.util.ActionSubscriber.onNext(ActionSubscriber.java:39)
   at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
    ... 9 more
Caused by: java.lang.ClassCastException: libcore.reflect.ParameterizedTypeImpl cannot be cast to java.lang.Class
   at com.github.jasminb.jsonapi.retrofit.RetrofitType.<init>(RetrofitType.java:23)
   at com.github.jasminb.jsonapi.retrofit.JSONAPIConverterFactory.responseBodyConverter(JSONAPIConverterFactory.java:45)
   at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:325)
   at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:308)
   at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:704)
    ... 18 more

It appears that the JSONAPIConverterFactory cannot handle nested parameterized types - it assumes that the top-level Type (in this case, JSONAPIDocument) is a collection, and the parameterized type is the actual type.

I have also tried this with the 0.3 release (using this Retrofit 2 adapter) with the same outcome.

How to handle class hierarchy?

Hi there,

How can we handle a situation where we want to receive a collection of objects, but with different sub-types? For instance a list of vehicles where we have, cars, bikes, trucks...

All the vehicles should have the same type: vehicle and we add an attribute "vehicle_type" or each one should have a different type (car, bike, truck)?

How can we handle each of the previous approaches with this library using Retrofit requests?

Thanks for your help

Null pagination links represented as "null" instead of a null object.

The JSON null value carries semantics, as in pagination links:

  "links": {
    "first": "http://localhost:8000/v2/nodes/",
    "last": "http://localhost:8000/v2/nodes/?page=m",
    "prev": null,
    "next": "http://localhost:8000/v2/nodes/?page=2"
  }

Currently a null link value will be represented as the string "null" instead of a null object reference.

Recursive loops may occur during resolution

Models may introduce loops when resolving relationships:

@type(...)
class Node {
@relationship(...)
Node parent;
// etc.
}

If the JSON for this Node has a parent relationship pointing to itself, the stack blows.

Even if the author makes poor modeling decisions, probably some protections should be put in place to avoid blowing the stack.

Can't add JSONAPIConverterFactory to retrofit

I'm using the following code

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(getResources().getString(R.string.base_url))
                .addConverterFactory(new JSONAPIConverterFactory(new ObjectMapper(), User.class, Friendship.class))
                .client(client)
                .build();

Essentially the way it's done in the readme.

However there is a type mismatch error

Error:(89, 38) error: incompatible types: JSONAPIConverterFactory cannot be converted to Factory

I'm using retrofit 2.0.1 and jsonapi-converter 0.2. I wonder if the apis have changed since the example in the readme or am I just missing something here?

Any ideas?

v0.3 tag

Is the v0.3 release tag purposely missing or just forgotten?

JSONAPIConverterFactory cannot support Retrofit to have multiple converters

Hi,

As defined by Retrofit Converters, if a factory cannot handle the given type, fromResponseBody and toRequestBody it should return null so other factories can be tried : https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Converter.java

Since some of the responses I need to parse doesn't follow jsonapi, my only way to cope with that is to have a second retrofit object in my dependency injection graph.

Type annotation doesn't allow name

Hi,

I don't know why, but I can't give @Type a name like @Type(name = "MyClass"). It only allows me @Type("MyClass"), but it seems like that it doesn't work. :(

Thanks,
Max

Error when parsing null primary data

I am trying to retrieve a resource from an endpoint which may or may not exist (where not existing is a valid state). When the resource does not exist, the following object is returned:

{
  "data": null
}

According to the JSON-API spec, this is valid ("Primary data MUST be... a single resource object, a single resource identifier object, or null, for requests that target single resources"). However, deserialization of this object fails with the error: "'data' node cannot be simple attribute!".

Meta field with data being a collection

Hello,

it seems impossible to me to parse meta information when the json data is a collection.

How to parse meta info for a JSON such as :

{
  "data": [
    {
      "id": "11",
      "type": "address",
      "attributes": {
        "city": "Lausanne",
        ...
      }
    },
    {
      "id": "12",
      "type": "address",
      "attributes": {
        "city": "Bern",

    ...
      }
    }
  ],
  "jsonapi": {
    "version": "1.0"
  },
  "meta": {
    "default_billing_address_id": "11",
    "default_shipping_address_id": "12"
  }
}

Do you see a solution ?

cheers

Parse jsonapi requests

In the short documentation of this lib, it says that it can be used for "request/response", although i believe, and please correct me if i'm wrong, that it's only for parsing jsonapi responses. Are you planning on implementing request parsing as well? Thanks

Consider katharsis.io annotation support

http://katharsis.io is a server-side jsonapi implementation. It would be great if katharsis and this project could agree on a common set of annotations so that classes do not need to be annotated for both libraries seperately if both client and server are written in Java. I created a ticket here to start.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.