Modify default JSON error response from Spring Boot Rest Controller - json

Currently the error response from spring boot contains the standard content like below:
{
"timestamp" : 1426615606,
"exception" : "org.springframework.web.bind.MissingServletRequestParameterException",
"status" : 400,
"error" : "Bad Request",
"path" : "/welcome",
"message" : "Required String parameter 'name' is not present"
}
I am looking for a way to get rid of the "exception" property in the response. Is there a way to achieve this?

As described in the documentation on error handling, you can provide your own bean that implements ErrorAttributes to take control of the content.
An easy way to do that is to subclass DefaultErrorAttributes. For example:
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
// Customize the default entries in errorAttributes to suit your needs
return errorAttributes;
}
};
}

If there is empty message text in json when you encounter exception, you can be hit by changed behavior in spring boot 2.3.0. If this is the case, just change your server.error.include-message property to always.

Following answer is totally derived from Andy Wilkinson's answer (which uses web.reactive classes)
- It includes web.servlet based classes.
- Spring boot 2.2.4.RELEASE
ExceptionHandlerConfig.java
package com.example.sample.core.exception;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.WebRequest;
#Configuration
public class ExceptionHandlerConfig {
//private static final String DEFAULT_KEY_TIMESTAMP = "timestamp";
private static final String DEFAULT_KEY_STATUS = "status";
private static final String DEFAULT_KEY_ERROR = "error";
private static final String DEFAULT_KEY_ERRORS = "errors";
private static final String DEFAULT_KEY_MESSAGE = "message";
//private static final String DEFAULT_KEY_PATH = "path";
public static final String KEY_STATUS = "status";
public static final String KEY_ERROR = "error";
public static final String KEY_MESSAGE = "message";
public static final String KEY_TIMESTAMP = "timestamp";
public static final String KEY_ERRORS = "errors";
//
#Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
#Override
public Map<String ,Object> getErrorAttributes(
WebRequest webRequest
,boolean includeStackTrace
) {
Map<String ,Object> defaultMap
= super.getErrorAttributes( webRequest ,includeStackTrace );
Map<String ,Object> errorAttributes = new LinkedHashMap<>();
// Customize.
// For eg: Only add the keys you want.
errorAttributes.put( KEY_STATUS, defaultMap.get( DEFAULT_KEY_STATUS ) );
errorAttributes.put( KEY_MESSAGE ,defaultMap.get( DEFAULT_KEY_MESSAGE ) );
return errorAttributes;
}
};
}
}

Related

How can I implement I18n as a JSON object for a JPA or Hibernate entity?

I have a Spring-boot application that uses JPA and Hibernate. You can find the whole code on this GitHub repository.
My question is how can I add internationalization functionality to a specific column without any foreign keys and by using JSON structure?
For example I would like to define a JPA entity like this:
#Entity
class Book {
#Id
private int id;
private Author author;
#I18n //<- this annotation is something that I am looking for
private String title;
}
and then the data in title column would be stored like the following for en and de locales:
{"en":"Cologne","de":"Köln"}
And then when the current locale is de the Köln and when the en is set as locale then Cologne fetch in the time of reading data!
Also when we store the data, the passed string is stored in the relevant property in the JSON format. For example if the locale is set to es and user passes Kolne then we have to have the following data in the DB:
{"en":"Cologne","de":"Köln","es":"Kolne"}
It is interesting for me that most of the solutions in the web for hibernate and JPA is based on an old method that we have languages and translations tables. Something like here or here.
However what I am looking for is some solutions like this one which is suggested for Laravel and store the translations exactly in the way that I explained (i.e. in a JSON object and in the same column)!
The only solution that I found and could be somehow relevant (Not 100%) is this one, however it does not working when I tried to test it and it seems does not supported anymore!
Hibernate Types project
First, you need to add the Hibernate Type project dependency.
Afterward, you could use either an HStore or a JSONB column to store the locate-specific titles:
#Entity
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
class Book {
#Id
private int id;
private Author author;
#Type(type = "jsonb")
#Column(name = "localized_titles", columnDefinition = "jsonb")
private Map<String, String> localizedTitles = new HashMap<>();
public String getLocalizedTitle(String locale) {
return localizedTitles.get(locale);
}
public String getLocalizedTitle() {
return localizedTitles.get(LocaleUtil.getDefaultLocale());
}
}
So, you can call the getLocalizedTitle and pass the current locale to get the current localized title.
Book book = entityManager.find(Book.class, bookId);
String title = book.getLocalizedTitle("en");
Or, you could store the current locale in a ThreadLocal in a class called LocaleUtil:
public class LocaleUtil {
private static final ThreadLocal<String> LOCALE_HOLDER =
new ThreadLocal<>();
public static String getLocale() {
return LOCALE_HOLDER.get();
}
public static void setLocale(String locale) {
LOCALE_HOLDER.set(locale);
}
public static void reset() {
LOCALE_HOLDER.remove();
}
}
And store the current locale like this:
LocaleUtil.setLocale("en");
And, then just call the getLocalizedTitle method that takes no argument:
Book book = entityManager.find(Book.class, bookId);
String title = book.getLocalizedTitle();
Check out this PostgreSQLJsonMapTest test case on GitHub for more details about using Hibernate Types to persiste Java Map as JSON column types.
After some weeks I could return back again to my olingo2 odata server project.
What I wanted to do was simpler than what I expected.
The solution has been suggested by Vlad Mihalcea is good and I appreciate it, however as I mentioned in the question I need a solution that works beside of the Olingo JPA library! However, the suggested solution has this problem that Olingo cannot handle JsonBinaryType.
Here is my suggestion for implementing internationalization beside of Olingo JPA.
Assume we have a BasicModel.java like this:
import java.io.Serializable;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import java.io.IOException;
public abstract class BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
private static ObjectMapper mapper = new ObjectMapper();
#SuppressWarnings("unchecked")
protected static Map<String, String> jsonToMap(String json) {
Map<String, String> map = new HashMap<>();
try {
// convert JSON string to Map
if (json != null) {
map = (Map<String, String>) mapper.readValue(json, Map.class);
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
protected static String mapToJson(Map<String, String> map) {
String json = "";
try {
// convert map to JSON string
json = mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return json;
}
protected static String getLang() {
Locale currentLocale = LocaleContextHolder.getLocale();
String[] localeStrings = (currentLocale.getLanguage().split("[-_]+"));
return localeStrings.length > 0 ? localeStrings[0] : "en";
}
}
This class provides a mechanism for us to convert JSON strings to Map and vice versa.
The code for converters had been adapted from here. For using this snippet of code we need to add this maven dependency:
<!-- Convert JSON string to Map -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
Finally, whenever in a JPA entity model we want to have i18n for a string property we only need to modify setter and getter methods slightly. For example:
import javax.persistence.*;
import java.util.Map;
import java.util.Set;
/**
* The persistent class for the actions database table.
*
*/
#Entity
#Table(name = "actions")
#NamedQuery(name = "Action.findAll", query = "SELECT a FROM Action a")
public class Action extends BaseModel {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 255)
private String id;
#Column(nullable = false, length = 255)
private String name;
public Action() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
Map<String, String> map = jsonToMap(this.name);
return map.get(getLang());
}
public void setName(String name) {
Map<String, String> map = jsonToMap(this.name);
map.put(getLang(), name);
this.name = mapToJson(map);
}
}

How to solve "Error while extracting response for type [class com.*" in Spring Boot?

You might want to skip to my UPDATE 2 bellow
I have a RestController that works, because when I access it directly from the browser, it returns a JSON response. However, when I send a request from a Service in a different bounded context, I get the error:
{"timestamp":1579095291446,"message":"Error while extracting response for type
[class com.path.to.contexttwo.client.dto.WorkerDetails] and content type [application/json]; nested exception is
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false');
nested exception is com.fasterxml.jackson.core.JsonParseException:
Unexpected character ('<' (code 60)):
expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n at [Source: (PushbackInputStream);
line: 1, column: 2]","details":"uri=/context-two/register-new"}
Here is my code:
RestController
package com.path.to.contextone.aplication.presentation;
#RestController
#RequestMapping(path = "/iacess", produces = "application/json")
#CrossOrigin(origins = "*")
public class IAccessRestController {
UserRepository userRepo;
IAcessService iaccessService;
EntityLinks entityLinks;
#Autowired
public IAccessRestController(
UserRepository userRepo,
IAcessService iaccessService,
EntityLinks entityLinks) {
this.userRepo = userRepo;
this.iaccessService= iaccessService;
this.entityLinks = entityLinks;
}
#GetMapping("/get-worker-details/{userName}")
public WorkerDetails getWorkerDetails(#PathVariable String userName) {
User user = userRepo.findByUsername(userName);
WorkerDetails workerDetails = new WorkerDetails();
workerDetails.setUserId(userId);
workerDetails.setGender(user.gender());
workerDetails.setFirstName(user.getFirstName());
workerDetails.setLastName(user.getLastName());
workerDetails.setPhoneNumber(user.getPhoneNumber());
if (workerDetails != null) {
return workerDetails;
}
return null;
}
}
RestClient
package com.path.to.contexttwo.client;
// imports omitted, as well as other code
#Service
public class IAcessRestClientImpl implements IAcessRestClient {
private final RestTemplate restTemplate;
#Autowired
public IAcessRestClientImpl(
final RestTemplate restTemplate
) {
this.restTemplate = restTemplate;
}
#Override
public WorkerDetails getWorkerDetailsByName(final String userName) throws URISyntaxException {
Map<String,String> urlVariables = new HashMap<>();
urlVariables.put("userName", userName);
return restTemplate.getForObject(
"http://localhost:8080/iacess/get-worker-details/{userName}",
WorkerDetails.class,
urlVariables
);
}
}
Config
package com.path.to.contexttwo.configuration;
#Configuration
#EnableWebMvc
public class RestClientConfig {
#Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypes);
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
restTemplate.getInterceptors().add(new JsonInterceptor());
return restTemplate;
}
}
WorkerDetails
package com.path.to.contexttwo.client.dto;
import java.io.Serializable;
import java.util.Objects;
public class WorkerDetails implements Serializable {
private long userId;
private String gender;
private String firstName;
private String lastName;
private String phoneNumber;
public WorkerDetails() {
this.userId = -1;
this.gender = null;
this.firstName = null;
this.lastName = null;
this.phoneNumber = null;
}
// omitted all args constructor, getters, setters, equals, hascode, toString for simplicity
}
WorkerDetails also exists in package com.path.to.contextone.ohs_pl;
I've been trying for 3 days, reading and debugging, to no avail. Debugger seems to show that the error happens when RestTemplate is analysing the WorkerDetails.class.
I also tried using ComponentScan in all configuration classes, because files are in separate packages (bounded contexts), without success.
I could just use the UserDetailsRepository from the class that calls IAcessRestClient to get the WorkerDetails, but this would make two different bounded contexts depend on the same database schema.
Any help would be very appreciated.
I can post aditional code per request.
Thanks in advance
UPDATE
#S B ask for input params. here goes the class that sends the params:
CompanyServiceImpl
package com.path.to.contexttwo.domain.services;
// imports
#Service
public class CompanyServiceImpl implements CompanyService {
private CompanyRepository companyRepository;
private CompanyWorkerRepositoery companyWorkerRepositoery;
private WorkerDetailsClient workerDetailsClient;
private WebApplicationContext applicationContext;
#Autowired
CompanyServiceImpl (
CompanyRepository companyRepository,
CompanyWorkerRepositoery companyWorkerRepositoery,
WorkerDetailsClient workerDetailsClient,
WebApplicationContext applicationContext
) {
this.companyRepository = companyRepository;
this.companyWorkerRepositoery = companyWorkerRepositoery;
this.workerDetailsClient = workerDetailsClient;
this.applicationContext = applicationContext;
}
#Transactional
public Company criateCompany(CompanyDTO dto) throws URISyntaxException {
if (dto.getLegalyAuthorized() == true && dto.getTerms() == true) {
Company company = new Company(
dto.getCompanyName(),
dto.getStateId()
);
company = CompanyRepository.save(company);
// when saving the company, we also need some details from the current logged in user which can be
// retrieved from the idendity and access bounded context. We need those details to be saved in this context
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName();
WorkerDetails workerDetails = WorkerDetailsClient.getWorkerDetailsByName(
name
);
// ... we can't reach the rest of the code anyway, so we omitted
}
}
And here is the response I get when acessing the RestController directly:
{"userId":127,"gender":"M","firstName":"Primeiro","lastName":"Último","phoneNumber":"922222222"}
UPDATE 2
Commented out .anyRequest().authenticated() and everything runned OK! So, it has to do with Spring Security all this time. What a shame. Will now try to make things work with security enabled. I was receiving HTML as response because of redirection to login page. Implemented authentication correctly (token request with basic auth) and everything works well.
Thank you all!
Try:
return restTemplate.getForObject(
"http://localhost:8080/iacess/get-worker-details/" + userName,
WorkerDetails.class);

Spring Boot adding attribute to XML element but NOT in JSON response

I am working on an API that produces both XML and JSON responses. I have one element in the response which requires an attribute only in XML response. Also, when the value is null, the element shouldn't show up in the response for both formats.
Expectation:
XML:
<name>john</name>
<status type="text">married</status>
JSON:
"name":"john"
"status":"married"
This is my code:
/**
* POJO with bunch of LOMBOK annotations to avoid boiler-plate code.
*/
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
#Data
public class User implements Customer, Serializable {
private static final long serialVersionUID = 1L;
private Status status;
private String name;
/**
* Matrital status of the user.
*/
#Builder
#Value
public static class Status {
#JacksonXmlText
private String maritalStatus;
#JacksonXmlProperty(isAttribute = true)
private String type = "text";
}
}
With the above change, I am getting the correct XML response but JSON response also returns type=text
"status" : {
"maritalStatus" : "married",
"type" : "text"
}
I tried to add #JsonValue to private String maritalStatus, that solved the JSON response but it broke XML response by not adding the attribute to the element.
Can someone please help?
Probably the easiest way is to implement custom serialiser for User.Status and produce different output for different kinds of representation.
class UserStatusJsonSerializer extends JsonSerializer<User.Status> {
#Override
public void serialize(User.Status value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (gen instanceof ToXmlGenerator) {
ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
serializeXml(value, toXmlGenerator);
} else {
gen.writeString(value.getMaritalStatus());
}
}
private void serializeXml(User.Status value, ToXmlGenerator toXmlGenerator) throws IOException {
toXmlGenerator.writeStartObject();
toXmlGenerator.setNextIsAttribute(true);
toXmlGenerator.writeFieldName("type");
toXmlGenerator.writeString(value.getType());
toXmlGenerator.setNextIsAttribute(false);
toXmlGenerator.writeRaw(value.getMaritalStatus());
toXmlGenerator.writeEndObject();
}
#Override
public boolean isEmpty(SerializerProvider provider, User.Status value) {
return value == null || value.getMaritalStatus() == null;
}
}
Since now, you can remove extra XML annotations and register custom serialiser:
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
#Data
class User implements Serializable {
private static final long serialVersionUID = 1L;
private Status status;
private String name;
#Builder
#Value
#JsonSerialize(using = UserStatusJsonSerializer.class)
public static class Status {
private String maritalStatus;
private String type = "text";
}
}
Simple console app usage could look like below:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Value;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
List<User> users = Arrays.asList(
createUser("John", "married"),
createUser("Tom", null));
ObjectMapper jsonMapper = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(jsonMapper.writeValueAsString(user));
System.out.println();
}
XmlMapper xmlMapper = XmlMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.serializationInclusion(JsonInclude.Include.NON_EMPTY)
.build();
for (User user : users) {
System.out.println(xmlMapper.writeValueAsString(user));
System.out.println();
}
}
private static User createUser(String name, String maritalStatus) {
return User.builder()
.name(name)
.status(User.Status.builder()
.maritalStatus(maritalStatus)
.build())
.build();
}
}
Above code prints
JSON for John:
{
"status" : "married",
"name" : "John"
}
JSON for Tom:
{
"name" : "Tom"
}
XML for John:
<User>
<status type="text">married</status>
<name>John</name>
</User>
XML for Tom
<User>
<name>Tom</name>
</User>
Notice, that we implemented UserStatusJsonSerializer#isEmpty method which defines what empty means for a Status class. Now, we need to enable JsonInclude.Include.NON_EMPTY feature in your Spring Boot application. Add below key to your application configuration file:
spring.jackson.default-property-inclusion=non_empty
If you do not want to enable inclusion globally you can enable it only for one property using #JsonInclude annotation.
#JsonInclude(JsonInclude.Include.NON_EMPTY)
private Status status;
See also:
Using Jackson to add XML attributes to manually-built node-tree
How to tell Jackson to ignore a field during serialization if its value is null?
Spring Boot: Customize the Jackson ObjectMapper
The solution to marshalling an object one way in XML, but another in JSON (different fields, etc.) was to use "mixins".
One trick is that you have to manually register the mixin, there's no magic. See below.
Mixin interface:
public interface UserStatusXmlMixin {
#JsonValue(false)
#JacksonXmlText
String getStatus();
#JacksonXmlProperty(isAttribute = true)
String getType();
}
Implementation:
#Value
public class UserStatus implements UserStatusXmlMixin {
private String status;
#JsonValue
#Override
public String getStatus() {
return status;
}
#Override
public String getType() {
return "text";
}
/**
* Returns an unmodifiable UserStatus when status is available,
* otherwise return null. This will help to remove this object from the responses.
*/
public static UserStatus of(final String status) {
return Optional.ofNullable(status)
.map(UserStatus::new)
.orElse(null);
}
}
I also had to register the "mixin" manually.
#Configuration
public class AppJacksonModule extends SimpleModule {
private static final long serialVersionUID = -1;
private final Map<Class, Class> mixinByTarget;
/**
* Construct an AppJacksonModule.
*/
public AppJacksonModule() {
super("AppJacksonModule");
this.mixinByTarget = Map.of(
UserStatus.class, UserStatusXmlMixin.class
);
}
#Override
public void setupModule(final SetupContext context) {
super.setupModule(context);
final ObjectCodec contextOwner = context.getOwner();
if (contextOwner instanceof XmlMapper) {
mixinByTarget.forEach(context::setMixInAnnotations);
}
}
Now wherever I needed to create UserStatus using UserStatus.of(..) if the input param is null, <status/> won't show up in the response.

how to get the value from json in android

Hi am getting the following json pentaho server,How i need to get date from this json
"queryInfo":{
"totalRows":"1"
},
"resultset":[
[
"09-09-2014"
]
],
"metadata":[
{
"colIndex":0,
"colType":"String",
"colName":"dt"
}
]
}
The best way is to use gson to deserilize the string:
Create a Class in a separate file.
import java.util.List;
import com.google.gson.annotations.SerializedName;
public class pentaho {
public QueryInfo queryInfo;
public static class QueryInfo {
public List<Result> metadata;
public static class Result {
#SerializedName("colIndex")
public String colIndexStr;
#SerializedName("colType")
public String colTypeStr;
#SerializedName("colName")
public String colNameStr;
}
}
}
In your Activity
public pentaho pentahoResult;
In your functions.
private void getJson(String jsonStr){
Gson gson = new Gson();
pentahoResult = gson.fromJson(jsonStr, pentaho.class);
Now you can go through each result if there is more than one by replacing the 0 with a loop variable like int i.
String MycolIndex = pentahoResult.d.results.get(0).colIndexStr;
String MycolType = pentahoResult.d.results.get(0).colTypeStr;
String MycolName = pentahoResult.d.results.get(0).colNameStr;
Have fun.

Getting an Unrecognized field "Status" (Class ektron.cms.jdbc.extractors.AssetDataResponse), not marked as ignorable error with Jersey Client API

I have the following JSON being returned from a RESTful Service
{
"Status": "Success",
"Success": true,
"Path": "D:\\Work\\Sites\\EKSites\\OnTrek\\privateassets\\0\\155\\156\\ceb3dc64-33ed-4e96-80a2-846120ecd9ee.pdf",
"Timestamp": "2013-03-27T18:35:23.8997358-04:00"
}
I am trying to deserialize the JSON into this data class:
package ektron.cms.jdbc.extractors;
#JsonPropertyOrder({ "Status", "Success", "Path", "Timestamp" })
public class AssetDataResponse {
#JsonProperty("Status")
private String Status;
#JsonProperty("Success")
private Boolean Success;
#JsonProperty("Path")
private String Path;
#JsonProperty("Timestamp")
private String Timestamp;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("Status")
public String getStatus() {
return Status;
}
#JsonProperty("Status")
public void setStatus(String Status) {
this.Status = Status;
}
#JsonProperty("Success")
public Boolean getSuccess() {
return Success;
}
#JsonProperty("Success")
public void setSuccess(Boolean Success) {
this.Success = Success;
}
#JsonProperty("Path")
public String getPath() {
return Path;
}
#JsonProperty("Path")
public void setPath(String Path) {
this.Path = Path;
}
#JsonProperty("Timestamp")
public String getTimestamp() {
return Timestamp;
}
#JsonProperty("Timestamp")
public void setTimestamp(String Timestamp) {
this.Timestamp = Timestamp;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperties(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Following is my client code:
package ektron.common.network;
//...
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
WebResource webResource =
client.resource(
String.format("http://%s:%d/%s","localhost",7605,"asset"));
String return =
webResource
.path("3f7078c4")
.path("ceb3dc64-33ed-4e96-80a2-846120ecd9ee")
.accept(MediaType.APPLICATION_JSON)
.get(String.class); //This piece works and I get my JSON response as
//indicated above
But if I change the above to:
AssetDataResponse resp =
webResource
.path("3f7078c4")
.path("ceb3dc64-33ed-4e96-80a2-846120ecd9ee")
.accept(MediaType.APPLICATION_JSON)
.get(AssetDataResponse.class);
I get the following error:
Unrecognized field "Status" (Class ektron.cms.jdbc.extractors.AssetDataResponse), not marked as ignorable
Is there any configuration on ClientConfig that I need to make to get the deserialization working correctly? Any help on this would be very much appreciated. I am .NET developer quite new to Java and am not so familiar with the Jersey framework. I have already checked the answer from a similar question and my case is different from the case listed there.
Client side Jars
annotations-1.3.9.jar
asm-3.1.jar
codemodel-2.4.1.jar
jackson-annotations-2.1.2.jar
jackson-core-2.1.3.jar
jackson-core-asl-1.9.11.jar
jackson-databind-2.1.3.jar
jackson-jaxrs-1.9.2.jar
jackson-mapper-asl-1.9.11.jar
jackson-xc-1.9.2.jar
jcommander-1.30.jar
jersey-apache-client-1.17.jar
jersey-atom-abdera-1.17.jar
jersey-client-1.17.jar
jersey-core-1.17.jar
jersey-guice-1.17.jar
jersey-json-1.17.jar
jersey-multipart-1.17.jar
jersey-server-1.17.jar
jersey-servlet-1.17.jar
jettison-1.1.jar
jsr311-api-1.1.1.jar
validation-api-1.0.0.GA.jar
I got the issue resolved. The issue was that I was importing classes from both versions of Jackson libraries as "Perception" had indicated. I can now use the latest stable version of Jackson libraries with Jersey client.
Thanks Perception for your suggestion.
Here is what my client code now looks like.
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
...
/**
* Wrapper class for a RESTful Service
*/
public class RestServiceWrapper
{
public ResponseData performRequest(
List<String> reqArgs,
ResponseType rType,
Class<?> dataType
)
{
ResponseData data = new ResponseData();
for(String arg: nullCheck(reqArgs))
{
if(StringUtils.isNotEmpty(arg))
{
webResource=webResource.path(arg);
}
}
data.data = webResource
.accept(MediaType.APPLICATION_JSON) //For now only JSON supported
.get(AssetDataResponse.class);
data.rType = ResponseType.JSON;
return data;
}
protected ServiceInfo svcInfo;
public RestServiceWrapper(ServiceInfo svcInfo) {
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
this.svcInfo = svcInfo;
this.webResource =
client.resource(
String.format("http://%s:%d/%s",svcInfo.Host,svcInfo.Port,svcInfo.EndPoint));
}
private static <T> Iterable<T> nullCheck(Iterable<T> iterable) {
return iterable == null ? Collections.<T>emptyList() : iterable;
}
protected Client client;
protected WebResource webResource;
}
I had to include the following line failing which I ran into the original error again
clientConfig.getClasses().add(JacksonJsonProvider.class);
the JacksonJsonProvider imported from
//jackson-jaxrs-json-provider-2.1.3.jar
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
My jars now look like.
annotations-1.3.9.jar
asm-3.1.jar
codemodel-2.4.1.jar
jackson-annotations-2.1.2.jar
jackson-core-2.1.3.jar
jackson-databind-2.1.3.jar
jackson-jaxrs-json-provider-2.1.3.jar
jcommander-1.30.jar
jersey-apache-client-1.17.jar
jersey-atom-abdera-1.17.jar
jersey-client-1.17.jar
jersey-core-1.17.jar
jersey-guice-1.17.jar
jersey-json-1.17.jar
jersey-multipart-1.17.jar
jersey-server-1.17.jar
jersey-servlet-1.17.jar
jettison-1.1.jar
jsonschema2pojo-core-0.3.5.jar
jsr311-api-1.1.1.jar
validation-api-1.0.0.GA.jar