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

Commit 03cb277

Browse files
committed
Merge branch 'resource-collection-pr' into develop-wip
2 parents 03abb40 + bd47d33 commit 03cb277

5 files changed

Lines changed: 585 additions & 4 deletions

File tree

src/main/java/com/github/jasminb/jsonapi/JSONAPISpecConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ public interface JSONAPISpecConstants {
1818
String ERRORS = "errors";
1919
String META = "meta";
2020
String HREF = "href";
21+
String PREV = "prev";
22+
String NEXT = "next";
23+
String FIRST = "first";
24+
String LAST = "last";
2125
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.jasminb.jsonapi;
2+
3+
import java.util.Collections;
4+
import java.util.Map;
5+
6+
/**
7+
* Models a JSON API Link object.
8+
*/
9+
public class Link {
10+
11+
private String href;
12+
13+
private Map<String, ?> meta = Collections.emptyMap();
14+
15+
public Link() {
16+
17+
}
18+
19+
public Link(String href) {
20+
this.href = href;
21+
}
22+
23+
public Link(String href, Map<String, ?> meta) {
24+
this.href = href;
25+
this.meta = meta;
26+
}
27+
28+
public String getHref() {
29+
return href;
30+
}
31+
32+
public void setHref(String href) {
33+
this.href = href;
34+
}
35+
36+
public Map<String, ?> getMeta() {
37+
return meta;
38+
}
39+
40+
public void setMeta(Map<String, ?> meta) {
41+
this.meta = meta;
42+
}
43+
44+
}

src/main/java/com/github/jasminb/jsonapi/ResourceConverter.java

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.github.jasminb.jsonapi;
22

33
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.core.JsonParser;
45
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.core.type.TypeReference;
7+
import com.fasterxml.jackson.databind.JavaType;
58
import com.fasterxml.jackson.databind.JsonNode;
69
import com.fasterxml.jackson.databind.ObjectMapper;
710
import com.fasterxml.jackson.databind.node.ArrayNode;
811
import com.fasterxml.jackson.databind.node.ObjectNode;
12+
import com.fasterxml.jackson.databind.type.MapType;
13+
import com.fasterxml.jackson.databind.type.TypeFactory;
914
import com.github.jasminb.jsonapi.annotations.Id;
1015
import com.github.jasminb.jsonapi.annotations.Links;
1116
import com.github.jasminb.jsonapi.annotations.Meta;
@@ -228,7 +233,7 @@ private <T> T readObjectInternal(byte [] data, Class<T> clazz, ResolverState res
228233
* @return collection of converted elements
229234
* @throws RuntimeException in case conversion fails
230235
*/
231-
public <T> List<T> readObjectCollection(byte [] data, Class<T> clazz) {
236+
public <T> ResourceList<T> readObjectCollection(byte [] data, Class<T> clazz) {
232237
return readObjectCollectionInternal(data, clazz, null);
233238
}
234239

@@ -241,7 +246,7 @@ public <T> List<T> readObjectCollection(byte [] data, Class<T> clazz) {
241246
* @return collection of converted elements
242247
* @throws RuntimeException in case conversion fails
243248
*/
244-
private <T> List<T> readObjectCollectionInternal(byte [] data, Class<T> clazz, ResolverState resolverState) {
249+
private <T> ResourceList<T> readObjectCollectionInternal(byte [] data, Class<T> clazz, ResolverState resolverState) {
245250

246251
try {
247252
JsonNode rootNode = objectMapper.readTree(data);
@@ -259,14 +264,25 @@ private <T> List<T> readObjectCollectionInternal(byte [] data, Class<T> clazz, R
259264
result.add(pojo);
260265
}
261266

262-
return result;
267+
ResourceList<T> wrapper = new ResourceList<>(result);
268+
269+
if (rootNode.has(LINKS)) {
270+
Map<String, Link> links = mapLinks(rootNode.get(LINKS));
271+
wrapper.setLinks(links);
272+
}
273+
274+
if (rootNode.has(META)) {
275+
Map<String, ?> meta = mapMeta(rootNode.get(META));
276+
wrapper.setMeta(meta);
277+
}
278+
279+
return wrapper;
263280
} catch (RuntimeException e) {
264281
throw e;
265282
} catch (Exception e) {
266283
throw new RuntimeException(e);
267284
}
268285

269-
270286
}
271287

272288
/**
@@ -514,6 +530,62 @@ private void handleRelationships(JsonNode source, Object object, Map<String, Obj
514530
}
515531
}
516532

533+
/**
534+
* Deserializes a <a href="http://jsonapi.org/format/#document-links">JSON-API links object</a> to a {@code Map}
535+
* keyed by the link name.
536+
* <p>
537+
* The {@code linksObject} may represent links in string form or object form; both are supported by this method.
538+
* </p>
539+
* <p>
540+
* E.g.
541+
* <pre>
542+
* "links": {
543+
* "self": "http://example.com/posts"
544+
* }
545+
* </pre>
546+
* </p>
547+
* <p>
548+
* or
549+
* <pre>
550+
* "links": {
551+
* "related": {
552+
* "href": "http://example.com/articles/1/comments",
553+
* "meta": {
554+
* "count": 10
555+
* }
556+
* }
557+
* }
558+
* </pre>
559+
* </p>
560+
*
561+
* @param linksObject a {@code JsonNode} representing a links object
562+
* @return a {@code Map} keyed by link name
563+
*/
564+
private Map<String, Link> mapLinks(JsonNode linksObject) {
565+
Map<String, Link> result = new HashMap<>();
566+
567+
Iterator<Map.Entry<String, JsonNode>> linkItr = linksObject.fields();
568+
569+
while (linkItr.hasNext()) {
570+
Map.Entry<String, JsonNode> linkNode = linkItr.next();
571+
Link linkObj = new Link();
572+
573+
linkObj.setHref(
574+
getLink(
575+
linkNode.getValue()));
576+
577+
if (linkNode.getValue().has(META)) {
578+
linkObj.setMeta(
579+
mapMeta(
580+
linkNode.getValue().get(META)));
581+
}
582+
583+
result.put(linkNode.getKey(), linkObj);
584+
}
585+
586+
return result;
587+
}
588+
517589
/**
518590
* Accepts a JsonNode which encapsulates a link. The link may be represented as a simple string or as
519591
* <a href="http://jsonapi.org/format/#document-links">link</a> object. This method introspects on the
@@ -533,6 +605,27 @@ private String getLink(JsonNode linkNode) {
533605
return linkNode.asText();
534606
}
535607

608+
/**
609+
* Deserializes a <a href="http://jsonapi.org/format/#document-meta">JSON-API meta object</a> to a {@code Map}
610+
* keyed by the member names. Because {@code meta} objects contain arbitrary information, the values in the
611+
* map are of unknown type.
612+
*
613+
* @param metaNode a JsonNode representing a meta object
614+
* @return a Map of the meta information, keyed by member name.
615+
*/
616+
private Map<String, ?> mapMeta(JsonNode metaNode) {
617+
JsonParser p = objectMapper.treeAsTokens(metaNode);
618+
MapType mapType = TypeFactory.defaultInstance()
619+
.constructMapType(HashMap.class, String.class, Object.class);
620+
try {
621+
return objectMapper.readValue(p, mapType);
622+
} catch (IOException e) {
623+
// TODO: log? No recovery.
624+
}
625+
626+
return null;
627+
}
628+
536629
/**
537630
* Creates relationship object by consuming provided 'data' node.
538631
* @param relationshipDataNode relationship data node

0 commit comments

Comments
 (0)