Serialize list of objects to Json, with Jackson & Spring-MVC - json

I'm working on a project in wich I need to return a list of objects in json format. I'm using Spring-mvc framework with jackson library in order to do the serialization.
I want a json structure containing directly the objects, not a 'name:array of objects'.
This is my simplified code:
#RequestMapping(method = RequestMethod.GET, value = "/clients")
public List getClients(
#RequestParam(value = "estat", required = false) String estat
throws Exception {
List<Clients> l = s.mdClients(estat);
return l;
}
This "return l" that you see goes directly to Jackson, and jackson converts 'l' into an structure like:
{
"ClientsList": [
{
"x": "2",
"y": "5"
}
]}
The problem is the root "ClientsList". I want to get this ouput without root:
{
[
{
"x": "2",
"y": "5"
}
]}
So, anyone could help? thanks in advance!

Try to add #ResponseBody in method declaration:
public #ResponseBody List getClients()

I've found the solution using #ResponseBody in my controller as #vacuum commented (thanks!):
#RequestMapping(method = RequestMethod.GET, value = "/clients")
public #ResponseBody List getClients(
#RequestParam(value = "estat", required = false) String estat
throws Exception {
List<Clients> l = s.mdClients(estat);
return l;
}
I also needed to change my output-conversion method, using
<mvc:annotation-driven />
in my servlet-context.xml, in order to use jackson library for the json conversion of my list.
The output now:
[
{
"x": "2",
"y": "5"
}
]

Related

How to parse the JSON returned by a REST API call into a collection of objects in JAX-RS?

I am fairly new to JAX-RS so bear with me on this question. I'm trying to consume a REST API using JAX-RS framework. In particular I am trying to invoke a HTTP GET method on a resource where the response entity will be in JSON format. Up until now I can parse the returned JSON into a customized class with the following code snippet;
WebTarget target = client.target(url);
Builder builder = target.request(MediaType.APPLICATION_JSON);
myClass obj = builder.get(myClass.class);
However, in my latest GET request the JSON return will be best abstracted as a collection of objects. I know in .NET this can be done fairly easily with
JsonConvert.DeserializeObject<List<myClass>>
but how could I do that in JAX-RS? Thanks in advance.
EDIT:
I model my code after the solution in How to get list<String> as response from jersey2 client
WebTarget target = client.target(url);
Builder builder = target.request(MediaType.APPLICATION_JSON);
builder.header(X_OCTOPUS_APIKEY_NAME, apiKey);
Response serviceResponse = builder.get(Response.class);
List<myType> objects = serviceResponse.readEntity(new GenericType<List<myType>>() {});
However the objects returned is always null. To verify the REST API call actually return a valid JSON value I replace the last line with:
String strDate = serviceResponse.readEntity(String.class);
It is confirmed with the following JSON return:
[
{
"Id": "Users-267",
"Username": "mdamon#mydomain.com",
"DisplayName": "Damon, Matt",
"IsActive": true,
"IsService": false,
"EmailAddress": "mdamon#mydomain.com",
"IsRequestor": false,
"Links": {
"Self": "/api/users/Users-267",
"Permissions": "/api/users/Users-267/permissions",
"ApiKeys": "/api/users/Users-267/apikeys{/id}{?skip}",
"Avatar": "https://www.gravatar.com/avatar/94324e7c54a9a5f9d103b2a709863fc3?d=blank"
}
},
{
"Id": "Users-2101",
"Username": "baffleck#mydomain.com",
"DisplayName": "Affleck, Ben",
"IsActive": true,
"IsService": false,
"EmailAddress": "baffleck#mydomain.com",
"IsRequestor": false,
"Links": {
"Self": "/api/users/Users-2101",
"Permissions": "/api/users/Users-2101/permissions",
"ApiKeys": "/api/users/Users-2101/apikeys{/id}{?skip}",
"Avatar": "https://www.gravatar.com/avatar/11edd32712facde9a7d3dd4445a4abe9?d=blank"
}
},
...
]
So for reason the JSON is not being parsed at a collection of my custom type. One extra piece of information is my custom is defined as follows:
#XmlRootElement
public class myType {
String DisplayName;
String EmailAddress;
public myType() {
super();
}
public void setDisplayName(String displayName) {
DisplayName = displayName;
}
public String getDisplayName() {
return DisplayName;
}
public void setEmailAddress(String emailAddress) {
EmailAddress = emailAddress;
}
public String getEmailAddress() {
return EmailAddress;
}
}
I only include the DisplayName and EmailAddress field of the JSON in my custom type because I don't need all the other data, in case that matters. Can anyone tell me why it is not being parsed? Thanks

Extract certain fields and values from request JSON using Jackson

I am looking for a way to include only certain fields from the JSON request received to my service and then log them accordingly to avoid logging too much data.
The fields to include and hence log would be configured in a properties file.
Since I use Spring Boot for my service, Jackson JARs should already be available.
Since the request JSON is complex with nested arrays and objects as fields am not sure if I can achieve my requirement with Jackson.
Is there a way to extract only certain fields along with their values from a input request JSON using the Jackson API?
Basically, I am looking at 2 use cases.
1.Select one or more elements (which I intend to pass by config) from Json String and then render them
2.Select and update one or more elements inline. I want to mask the values of the elements before rendering them.
I am providing the code for selecting the element and Json which I used along with what I expect as below.
public String getValues(String key,String jsonString){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
}
My Json is as below,
{
"employeeId" : "5353",
"salesId" : "sales005",
"manager" : {
"userId" : "managerUser",
"isSuperUser" : false,
"firstName":"manager first name",
"lastName":"manager last name"
},
"administrator" : {
"userId" : "administratorUser",
"isSuperUser" : false,
"firstName":"admin first name",
"lastName":"admin last name"
},
"requester" : [
{
"id":"1234",
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"id":"1235",
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
}
I have 2 issues as below.
If I pass "/manager/userId" ,"/administrator/isSuperUser" (OR) "/salesId" I am able to get the expected value.
But, If want to get the /requester/id (OR) /requester/demographic (OR) /requester/demographic/lastName , I am not able to fetch.
I am getting null values.
I expect the following when I pass , "/requester/id" (OR) "/requester/demographic" respectively.
"requester" : [
{
"id":"1234"
},
{
"id":"1235"
}
]
"requester" : [
{
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
Along with fetch I also want to update the values inline after locating them with JsonPointer
I have my code as below for the updation,
public String findAndUpdate(String key,String jsonString,String repValue){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
//Update the value of located node to a different one
((ObjectNode) fieldNode).put(key,repValue);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
return fieldNodeStr;
}
When I pass, "/manager/userId" as value of key, I get the below error,
17:21:24.829 [main] ERROR com.demo.jsondemo.TestClass - Exception:
java.lang.ClassCastException: com.fasterxml.jackson.databind.node.TextNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode
at com.demo.jsondemo.TestClass.findAndUpdate(TestClass.java:95) [classes/:na]
at com.demo.jsondemo.TestClass.main(TestClass.java:221) [classes/:na]
JSON Pointer
You could use JSON Pointer (a string syntax for identifying a specific value within a JSON document) defined by the RFC 6901.
For example purposes, consider the following JSON:
{
"firstName": "John",
"lastName": "Doe",
"address": {
"street": "21 2nd Street",
"city": "New York",
"postalCode": "10021-3100",
"coordinates": {
"latitude": 40.7250387,
"longitude": -73.9932568
}
}
}
To query the coordinates node, you could use the following JSON Pointer expression:
/address/coordinates
JSON Pointer and Jackson
Jackson 2.3.0 introduced support to JSON Pointer and it can be used as following:
String json = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"address\":{\"street\":"
+ "\"21 2nd Street\",\"city\":\"New York\",\"postalCode\":\"10021-3100\","
+ "\"coordinates\":{\"latitude\":40.7250387,\"longitude\":-73.9932568}}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
JsonNode coordinatesNode = node.at("/address/coordinates");
The coordinates node could be parsed into a bean:
Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class);
Or can be written as String:
String coordinatesNodeAsString = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(coordinatesNode);
Jayway JsonPath
A good alternative to JSON Pointer is JSONPath. In Java, there's an implementation called Jayway JsonPath, which integrates with Jackson.
To query the coordinates node with JsonPath, you would use the following expression:
$.address.coordinates
And the code to query the node would be like:
JsonNode coordinatesNode = JsonPath.parse(json)
.read("$.address.coordinates", JsonNode.class);
JsonPath requires the following dependency:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
And, to integrate with Jackson, following lines are required:
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
#Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
#Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
Update based on your requirements
Based on the additional details you have provided in your question, I would say JSONPath will offer you more flexibility the JSON Pointer.
You can try the following:
Instead of /requester/id, use $.requester[*].id.
Instead of /requester/demographic, use $.requester[*].demographic.
These expressions can be tested online. Have a look at the following resources:
JSONPath Online Evaluator
JSONPath Expression Tester
And read the Jayway JsonPath documentation to understand how to use it properly.
Jackson's JsonView annotations should allow you to mark certain fields/getters with a particular view. Something like the following should work.
public class Item {
public static class LoggingView {}
#JsonView(LoggingView.class)
public int id;
#JsonView(LoggingView.class)
public String name;
public byte[] data;
}
This should allow you to write id and name without writing data by doing the following:
public class Something {
private static final Logger logger = LoggerFactory.getLogger();
private static final ObjectWriter loggingItemWriter = new ObjectMapper().writerWithView(Item.LoggingView.class);
public void doSomething(Item item) {
...
logger.info(loggingItemWriter.writeValueAsString(item));
...
}
}

Deserialize json array and extract value using rest assured

I am a java, json, rest assured newbie and trying to work and learn how to test a rest api. I have an array returned as a rest assured response:
Response response = given(getProjectInfoRequest).get();
response.asString();
{
"options": [
{
"text": "111",
"label": "ABC"
},
{
"text": "222",
"label": "DEF"
},
{
"text": "333",
"label": "GHI"
}
]
}
and I want to extract the value of text say for label value as "DEF", how I can do that?
Please note I have done below so far after reading through so many posts:
1. Options[] options = given(getProjectInfoRequest).when().get().as(Options[].class);
this was giving me exception :
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
then I tried below:
2. Options options = gson.fromJson(response.asString(), Options.getClass());
this at least resolved the above issue.
public class Options {
public String getLabel() {
return label
}
public void setLabel(String label) {
this.label = label
}
public String getValue() {
return value
}
public void setValue(String value) {
this.value = value
}
public String label;
public String value;
}
From this point I am not sure how I can iterate through the array of text and values to extract what I need, please can you guys provide your inputs?
Please pardon my ignorance for asking such a basic question.
Please also suggest me a good source/way to learn this too.
Thanks in advance!
U can use Gson - This is a Java library that can be used to convert Java Objects into their JSON representation.
JsonParser parser = new JsonParser();
JsonObject o = (JsonObject)parser.parse(response.asString());
for (Map.Entry<String,JsonElement> entry : o.entrySet()) {
JsonArray array = entry.getValue().getAsJsonArray();
for (JsonElement elementJSON : array) {
[...]
}
}

How can I use the Gson to parse a json that keep the same key but not the same type

Actually,I want to parse the json like this:
{
"contents": [
{
"type": "image",
"data": {
"attachment": "picurl",
"width": 600,
"height": 398
}
},
{
"type": "text",
"data": "something like xxx"
}
]
}
as you can see, the key "data" sometimes is a String, and sometings is json object, what should I do to parse this json by Gson?
The way I do this is deserialize twice. First into an class that defines only type.
class TypeObject {
public String type;
}
After the first deserializiation, you can read the type, and discover which is the real target object you should deserialize into.
De-serializing twice obviously isn't ideal, but that's the unfortunate nature of using a static language to de-serialize a JSON string with non uniform objects in a list.
I have find the way the parse that json, as Alexis C said, I custom a deserializer, like that
#Override
public MessageDiv deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
MessageDiv messageDiv = new MessageDiv();
JsonObject obj = jsonElement.getAsJsonObject();
String msgtype = obj.get("type").getAsString();
messageDiv.setType(msgtype);
if (msgtype.equals("text")) {
messageDiv.setContent(obj.get("data").getAsString());
} else {
JsonObject imgObj = obj.get("data").getAsJsonObject();
DivData dd = new DivData();
dd.setAttachment(imgObj.get("attachment").getAsString());
dd.setHeight(imgObj.get("height").getAsInt());
dd.setWidth(imgObj.get("width").getAsInt());
messageDiv.setData(dd);
}
return messageDiv;
}
About how to custom o deserializer, click here, it's userful for me!

How to create proper JAXB mapping to make Jersey deserialization process happened

I have JSON response from WS:
[
{
"name": "Bobby",
"status": "single"
},
{
"name": "John",
"status": "married"
}
]
Here is my wrapper
#XmlRootElement(name = "users")
public class UserListWrapper {
private List<User> users;
#XmlElement(name = "user")
public List<User> getUsers() {
return users;
}
// getters and setters omitted
}
And User class
#XmlRootElement
class User {
private String name;
private String status;
// getters and setters omitted
}
The problem is when Jersey try to deserialize response to my wrapper object. It say
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of com.jersey.test.UserListWrapper out of START_ARRAY token
Seams that something wrong with my wrapper annotations. How can I fix them?
UPD
When I send
{
"user": [
{
"name": "Bob",
"status": "single"
},
{
"name": "Mike",
"status": "married"
}
]
}
all works fine. But I need this format
[
{
"name": "Bobby",
"status": "single"
},
...
]
UPD
Jersey Client code
HttpAuthenticationFeature authenticationFeature = HttpAuthenticationFeature.basic("user", "secret");
Client client = ClientBuilder
.newClient()
.register(authenticationFeature)
.register(JacksonFeature.class);
WebTarget target = client.target("http://localhost:8080/server/");
UserListWrapper entity;
Response resp;
resp = target.queryParam("u", "info")
.path("/rest/users")
.request()
.accept(APPLICATION_JSON)
.get();
entity = resp.readEntity(UserListWrapper.class);
Forget the UserListWrapper wrapper then. List<User> is perfect for the JSON array ( [] ) format. If you add the wrapper class, then yes you will need the extra JSON object layer ( {} ). This:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response createBook(List<User> users) {
is supported just fine (at least with Jackson - which you are using).
UPDATE
If the response from the server is coming as a JSON array, then you can still deserialize it as a List<User>. For example
WebResource resource = client.resource("...");
List<User> users = resource.get(new GenericType<List<User>>(){});
See this related post
UPDATE 2
Since you are using the JAX-RS 2 client API, you can use the overloaded readEntity, which accepts a GenericType argument also
List<User> user = response.readEntity(new GenericType<List<User>>(){});