Restructuring JSON with JAX-RS ReaderInterceptors and a little bit of JSON-B Magic
Tags: enterprisejava, client, java, jaxrs
Published: 2021-06-15 20:40:48

---
title: Restructuring JSON with JAX-RS ReaderInterceptors and a little bit of JSON-B Magic
published: true
date: 2021-06-09 18:50:42 UTC
tags: EnterpriseJava,client,java,JAXRS
canonical_url: https://andymc12.net/2021/06/09/restructuring-json-with-jax-rs-readerinterceptors-and-a-little-bit-of-json-b-magic/
---

Have you ever needed to consume a RESTful service but the data structure of the remote service just didn’t quite match with what you had in mind in your client application?  I ran into this situation earlier this week while exploring some commercial REST APIs. In my case, I had some JSON like this:

```
{
  "links":{
    "self":"https://api.myhost.com/services/v2/music?titleSearch=10%2C000"
  },
  "data":[
    {
      "type":"Song",
      "id":"56780987",
      "attributes":{
        "admin":"EMI Christian Music Publishing",
        "author":"Jonas Myrin and Matt Redman",
        "ccli_number":6016351,
        "copyright":
          "2011 Thankyou Music, Said And Done Music, and SHOUT! Publishing",
        "created_at":"2014-11-10T17:31:26Z",
        "hidden":false,
        "last_scheduled_at":"2021-05-30T08:49:00Z",
        "last_scheduled_short_dates":"May 30, 2021",
        "notes":"Vocal Range B - D'",
        "themes":", Adoration, Blessing, Christian Life, Praise",
        "title":"10,000 Reasons (Bless The Lord)"
      },
      "links":{"self":"https://api.myhost.com/services/v2/music/8802060"}
    }
  ],
  "included":[],
  "meta":{
    "total_count":1,
    "count":1,
    "can_order_by":["title","created_at","updated_at","last_scheduled_at"],
    "can_query_by":["author","ccli_number","themes","title"],
    "parent":{
        "id":"132275",
        "type":"Organization"
    }
  }
}
```

This JSON looks like a nicely formed [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) response from a RESTful service providing song information. However, there are a few obstacles if we want to simply convert it into a Java object in our application like this:

```
public class Song {
    private String id;
    private String title;
    private String author;
    private LocalDate lastScheduled;
    // ... more fields, and public getters/setters ...
}
```

The first obstacle is that the object returned in the JSON response is not a `Song` object – it is a complex object that contains the song data (under the `data` field) and other RESTful navigational fields like `links` and `meta`.  To overcome this obstacle, we could create some sort of `MetaSong` object that has fields for `data`, `links`, etc., but unless we plan to use those other fields that seems like a waste. Fortunately there is a better way!

Upon receiving the response, we could intercept the JSON data and reformat it before the JAX-RS client or MicroProfile Rest Client interface converts the JSON to a Java object. We do that with a `ReaderInterceptor` provider, like this:

```
public class DataExtractionReaderInterceptor implements ReaderInterceptor {

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext context)
        throws IOException, WebApplicationException {

        InputStream is = context.getInputStream();
        JsonObject json = Json.createReader(is).readObject();
        JsonValue data = json.get("data");
        is = new ByteArrayInputStream(data.toString().getBytes())
        context.setInputStream(is);
        return context.proceed();
    }
}
```

This code is executed on the client after the response is returned to the client but before the response is converted to a Java object. It uses JSON-P APIs to read the response stream and then extracts the data field, then replaces the response stream with a new stream that only includes that data object.

The second obstacle is that the song’s JSON fields don’t match the Java object’s fields. The JSON has a field called attributes, and the relevant fields like `title`, `author`, etc. are all child fields of that.  But in the Java object, these fields are all directly under the `Song` class. We can overcome this obstacle with a little JSON-B magic:

```
public class Song {
    private String id;
    private String title;
    private String author;
    //...

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }

    public void setAttributes(Map<String,Object> attrs) {
        setTitle((String)attrs.get("title"));
        setAuthor((String)attrs.get("author"));
        // ...
    }
}
```

By default, JSON-B maps the JSON fields to Java fields by calling the appropriate setter method (e.g., JSON `id` is a `String` and would map to the `setId` Java method). However, since we don’t want to create a separate `Attributes` Java class to handle the JSON child fields, we instead create a placeholder method, `setAttributes` that takes a `Map<String, Object>` parameter.  Now, JSON-B will pass in the JSON attributes object as key-value pairs that we can then map to the appropriate fields on our `Song` Java object.

Now all we need to do is invoke the service using either the JAX-RS Client or MicroProfile Rest Client APIs. The full source code including a runnable sample is available at [https://github.com/andymc12/sample-restructure-json-data](https://github.com/andymc12/sample-restructure-json-data).

Thanks for checking in! As always, please let me know if you have any questions or comments.