When using Spring Boot @RestController, we are able to return directly an Entity in json format, given that we are able to convert our Entity Class object to Json using Jackson, however if using a JSONObject in our entity like in the last post

//User.java
@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    private Long id;

    @NonNull
    private String username;

    @NonNull
    @Column(columnDefinition = "TEXT")
    @Convert(converter= JSONObjectConverter.class)
    private JSONObject jsonData;
}

Results in an error similar to the one below:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.json.JSONObject]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.json.JSONObject and no properties discovered to create BeanSerializer 

Using Jackson Annotations

So, given that Springboot uses Jackson by default to convert our Entity Object to JSON to return when using @RestController we can make use of Jackson Annotations: @JsonIgnore and @JsonProperty

So now lets annotate our JsonObject with @JsonIgnore to avoid serializing that specific attribute by default.

...
@NonNull
    @Column(columnDefinition = "TEXT")
    @Convert(converter= JSONObjectConverter.class)
    @JsonIgnore
    private JSONObject jsonData;

However, this results in our attribute named jsonData not being returned by our controller.

So how can we return an object that may be serialized to Json by Jackson.

Using @JsonProperty annotation so that we have a method to convert our attribute to a Jackson friendly object.

Below our jsonData attribute we will create the following method to Convert a JSONObject to a HashMap annotated with @JsonProperty:

//This is the name of our attribute
 @JsonProperty("jsonData")
    public List<Object> getAsJsonString() throws IOException, JSONException {
        return toMap(activity);
    }

    //Reference for below code: https://stackoverflow.com/questions/21720759/convert-a-json-string-to-a-hashmap
    public Map<String, Object> toMap(JSONObject object) throws JSONException {
        Map<String, Object> map = new HashMap<String, Object>();

        Iterator<String> keysItr = object.keys();
        while(keysItr.hasNext()) {
            String key = keysItr.next();
            Object value = object.get(key);

            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            map.put(key, value);
        }
        return map;
    }

    public List<Object> toList(JSONArray array) throws JSONException {
        List<Object> list = new ArrayList<Object>();
        for(int i = 0; i < array.length(); i++) {
            Object value = array.get(i);
            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            list.add(value);
        }
        return list;
    }
}

So now we are able to return our JsonObject along with our other attributes in our Entity directly from our @RestController, and resulting in a full Json concatenated from our different attributes.

Our full entity would look like this

//User.java
@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    private Long id;

    @NonNull
    private String username;
    
    @NonNull
    @Column(columnDefinition = "TEXT")
    @Convert(converter= JSONObjectConverter.class)
    @JsonIgnore
    private JSONObject jsonData;

//This is the name of our attribute
 @JsonProperty("jsonData")
    public Map<String, Object> getAsJsonString() throws IOException, JSONException {
        return toMap(jsonData);
    }

    //Reference for below code: https://stackoverflow.com/questions/21720759/convert-a-json-string-to-a-hashmap
    public Map<String, Object> toMap(JSONObject object) throws JSONException {
        Map<String, Object> map = new HashMap<String, Object>();

        Iterator<String> keysItr = object.keys();
        while(keysItr.hasNext()) {
            String key = keysItr.next();
            Object value = object.get(key);

            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            map.put(key, value);
        }
        return map;
    }

    public List<Object> toList(JSONArray array) throws JSONException {
        List<Object> list = new ArrayList<Object>();
        for(int i = 0; i < array.length(); i++) {
            Object value = array.get(i);
            if(value instanceof JSONArray) {
                value = toList((JSONArray) value);
            }

            else if(value instanceof JSONObject) {
                value = toMap((JSONObject) value);
            }
            list.add(value);
        }
        return list;
    }
}

And that’s it, thank you for reading, hope it helped you.

If it helped, bookmark or share it with others who might face similar issues.

André Ilhicas dos Santos

Devops Padawan, curious about systems automation, learning new languages, paradigms tools each day.

ilhicas ilhicas


Published