Json Parse Error. Can not Deserialize from Object Value - json

There was an unexpected error (type=Bad Request, status=400).
JSON parse error:
Can not construct instance of javax.xml.bind.JAXBElement: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?);
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of javax.xml.bind.JAXBElement: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.PushbackInputStream#da18a91; line: 1, column: 622] (through reference chain:
Below is my code:
Here is my controller
#RestController
#RequestMapping(value = { "/api" })
public class TestController {
/*
* #Autowired BeanTest bean;
*/
private final static QName _PersonalNameTypeGivenNames_QNAME = new QName("http://sita.aero/iborders/evisa/Common/V1", "GivenNames");
private final static QName _PersonalNameTypeFamilyName_QNAME = new QName("http://sita.aero/iborders/evisa/Common/V1", "FamilyName");
#RequestMapping(value="/getBeanTest", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getTestBean() {
BeanTest bean = new BeanTest();
List<BeanTest> beanTest = new ArrayList<BeanTest>();
final List<JAXBElement<String>> jaxbElements = new ArrayList<JAXBElement<String>>();
JAXBElement<String> jaxbElementGivenNames = new JAXBElement<String>(_PersonalNameTypeGivenNames_QNAME, String.class, BeanTest.class, "Gaurav");
jaxbElements.add(jaxbElementGivenNames);
JAXBElement<String> jaxbElementFamilyNames = new JAXBElement<String>(_PersonalNameTypeFamilyName_QNAME, String.class, BeanTest.class, "Chhimwal");
jaxbElements.add(jaxbElementFamilyNames);
bean.getContent().addAll(jaxbElements);
return new ResponseEntity<Object>(bean,HttpStatus.OK);
}
}
My Entity is
public class BeanTest {
protected List<JAXBElement<String>> content;
public List<JAXBElement<String>> getContent() {
if (content == null) {
content = new ArrayList<JAXBElement<String>>();
}
return this.content;
}
}
I am calling this service from another rest api. Getting this error while calling the service:
#RestController
#RequestMapping(value = { "/api" })
public class SpringRestController {
#Autowired
private RestTemplateBuilder restTemplateBuilder;
#Autowired
public Environment env;
#GetMapping(value = "/getApplicationTest", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getApplicationTest() {
RestTemplate restTemplate = restTemplateBuilder.build();
BeanTest obj = restTemplate.getForObject("http://localhost:1010/api/getBeanTest",
BeanTest.class);
return new ResponseEntity<Object>(obj, HttpStatus.OK);
}
}

Related

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);

Status 400 and Error deserializing List of Objects. No default constructor found

I have this Spring Repository:
public interface MessageRepository extends CrudRepository<MessageObject, String>{
public List<MessageObject> findByEmisorOrDestinatario(String emisor, String destinatario);
}
My DAO is:
#Entity
#Table(name = "messages")
public class MessageObject implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String emisor;
private String destinatario;
private String mensaje;
private String tipo;
#JsonFormat(pattern="yyyy-MM-dd")
private LocalDate fecha;
private String id_housing;
public MessageObject() {
}
Now in my Controller I want to receive the Get request and search in my DB so:
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public List<MessageObject> enviados (#RequestParam("mail") String mail) {
return daoMsg.findByEmisorOrDestinatario(mail, mail);
}
}
Now I can call the service from my client, so:
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient();
WebTarget webResource = client.target("http://localhost:8082").path("/Mensajes").queryParam(mail);
Invocation.Builder invocationBuilder = webResource.request(MediaType.APPLICATION_JSON);
Response respuesta = invocationBuilder.get();
int status = respuesta.getStatus();
System.out.println(status);
MessageObject[] listMessages = respuesta.readEntity(MessageObject[].class);
Problems: I'm receiving a 400 status code. Also an error deserializing entityRead. Doing the request with Postman returns no errors and return the list of objects in JSON format.
StackTrace:
javax.ws.rs.ProcessingException: Error deserializing object from entity
stream. Caused by: javax.json.bind.JsonbException: Can't create instance of
a class: class [LMessages.MessageObject;
No default constructor found. Caused by: java.lang.NoSuchMethodException:
[LMessages.MessageObject;.<init>()
Question: how can I know where is my code failing? am I using the service invocation well?
Things I tried: changing Mediatype to GenericType
EDIT I tried removing the / from the path, still getting status 400
Solved. Problem was I was using .queryparam without key-value structure. So changing .queryparam(mail) to .queryparam("mail", mail) solved it.
Try to test with this :
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
#ReponseBody
public ResponseEntity<List<MessageObject>> enviados (#RequestParam("mail") String mail) {
return new ResponseEntity<>(daoMsg.findByEmisorOrDestinatario(mail, mail), HttpStatus.OK);
}

Spring boot Jackson Json Serialization for List implementation class (PagedList)

Is there any way to add serialization for list implementing class having custom attributes?
I am working on Rest service using Spring-boot 1.3. I have to return JSON response as Paged-List or Normal-List, depend on request on Controller. So, I have to keep return type of controller method as generic public List<Employee> getEmployees(int departmentId)
I am implementing list as below (using generics to use for different object lists)
public class PagedList<E> implements List<E> {
private List<E> list;
private long totalRecords; //Getter-setters are added
public PagedList(List<E> list) {
super();
this.list = list;
}
public PagedList(List<E> list, long totalRecords) {
super();
this.list = list;
this.totalRecords = totalRecords;
}
#Override
public boolean add(E element) {
return this.list.add(element);
}
//All other List abstract methods implemented same as above using this.list
}
Added JsonSerializer for same: public class PagedListSerializer extends JsonSerializer<PagedList> with serialization logic in serialize() method. Which is registered using spring-boot jackson customization :
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializerByType(PagedList.class, new PagedListSerializer());
return builder;
}
When I try to return PagedList<Employee>(list, 1000), I am not able to get following response. Its returning same as of normal list. Not executing custom serialization. How to get following paged response?
{
list : [{employeeId: "1", name: "John" }, ... ],
totalRecords : 1000
}
You probably don't need custom deserializer to get this json. Just add #JsonFormat(shape = JsonFormat.Shape.OBJECT) annotation to your class:
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static class PagedList<E> implements List<E> {
#JsonProperty
private List<E> list;
#JsonProperty // no need for this if you have getter-setters
private long totalRecords;
#JsonIgnore
#Override
public boolean isEmpty() {
return false;
}
...
Here is full demo: https://gist.github.com/varren/35c4ede769499b1290f98e39a2f85589
Update after comments:
I think Spring uses Jacksons return mapper.writerFor(List.class).writeValueAsString(new MyList()); Here is demo:
#RestController
#RequestMapping(value="/")
public static class MyRestController {
private static final ObjectMapper mapper = new ObjectMapper();
//returns [] for both 0 and 1
#RequestMapping(value="test", method = RequestMethod.GET)
public List test(#RequestParam int user) {
return user == 0 ? new ArrayList(): new MyList();
}
//returns [] for 0 and expected custom {"empty": true} for 1
#RequestMapping(value="testObj", method = RequestMethod.GET)
public Object testObj(#RequestParam int user) {
return user == 0 ? new ArrayList(): new MyList();
}
// returns expected custom {"empty": true}
#RequestMapping(value="testMyList", method = RequestMethod.GET)
public MyList testMyList() {
return new MyList();
}
// returns expected custom {"empty": true}
#RequestMapping(value="testMyListMapper", method = RequestMethod.GET)
public String testMyListMapper() throws JsonProcessingException {
return mapper.writeValueAsString(new MyList());
}
// returns []
#RequestMapping(value="testMyListMapperListWriter", method = RequestMethod.GET)
public String testMyListMapperListWriter() throws JsonProcessingException {
return mapper.writerFor(List.class).writeValueAsString(new MyList());
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static class MyList extends ArrayList {}
So you have to Option 1) return Object instead of List or Option 2) register custom serialifer for List (and not for PageList) builder.serializerByType(List.class, new PagedListSerializer()); like this:
public class PagedListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List valueObj, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if (valueObj instanceof PagedList) {
PagedList value = (PagedList) valueObj;
gen.writeStartObject();
gen.writeNumberField("totalRecords", value.getTotalRecords());
gen.writeObjectField("list", value.getList());
gen.writeEndObject();
}else{
gen.writeStartArray();
for(Object obj : valueObj)
gen.writeObject(obj);
gen.writeEndArray();
}
}
}
You can Create your customObject Mapper and use your serializer there.
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="custom.CustomObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

Jersey client read json response into custom object

public class RESTDataServiceClient{
private Client client;
private String dataServiceUri;
private String dataServiceResource;
private CustomData customData;
public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
this.client = client;
this.dataServiceUri = dataServiceUri;
this.dataServiceResource = dataServiceResource;
}
#Override
public CustomData getCustomData() {
WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
myCustomData = response.readEntity(CustomData.class);
return myCustomData;
}
}
CustomData.java
public class CustomData{
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
//getter and setter
}
sample json content
{"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }
I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
... 38 more
TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.
It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.
If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:
public class FakeTest {
#Test
public void test() throws Exception {
ObjectMapper om = new ObjectMapper();
om.registerModule(new CustomModule());
String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
CustomData cd = om.readValue(s, CustomData.class);
System.out.println(cd.getData());
}
public static class CustomData {
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }
public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
}
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomData.class, new CustomSerializer());
addDeserializer(CustomData.class, new CustomDeserializer());
}
public static class CustomSerializer extends JsonSerializer<CustomData> {
#Override
public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// add custom serializer here
}
}
public static class CustomDeserializer extends JsonDeserializer<CustomData> {
#Override
public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
ObjectNode value = (ObjectNode) entry.getValue();
Map.Entry<String, JsonNode> innerField = value.fields().next();
ObjectNode innerNode = (ObjectNode) innerField.getValue();
Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
TIntIntMap intMap = new TIntIntHashMap();
intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
data.put(Long.parseLong(entry.getKey()), innerMap);
}
CustomData customData = new CustomData();
customData.setData(data);
return customData;
}
}
}
}

Jackson deserialization - with contained ArrayList<T>

Good day,
I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:
{
"error":null,
"object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}
I have two classes to represent the type (the outer ListResponse):
public class ListResponse {
public String error;
public ArrayList<ListItem> object;
public ListResponse() {
}
}
and (the inner ListItem):
public class ListItem {
#JsonProperty("Id")
public int id;
#JsonProperty("Class")
public String classType;
#JsonProperty("ClassID")
public String classId;
#JsonProperty("ListItemParent_ID")
public int parentId;
#JsonProperty("Item")
public String item;
#JsonProperty("Description")
public String description;
#JsonAnySetter
public void handleUnknown(String key, Object value) {}
public ListItem() {
}
}
The class that invokes and returns the JSON looks like this:
public class CitizenPlusService {
private Client client = null;
private WebResource service = null;
public CitizenPlusService() {
initializeService("http://localhost:59105/PlusService/");
}
private void initializeService(String baseURI) {
// Use the default client configuration.
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
// Add a logging filter to track communication between server and client.
client.addFilter(new LoggingFilter());
// Add the base URI
service = client.resource(UriBuilder.fromUri(baseURI).build());
}
public ListResponse getListItems(String id) throws Exception
{
ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);
return response;
}
}
The important call here is the getListItems method. Running the code in a test harness, produces the following:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader#49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])
Please assist.
Regards,
Carl-Peter Meyer
You may be missing a #JsonDeserialize attribute as the type information does get lost in generics at run-time. Also you should avoid using concrete classes for collections if you can.
public class ListResponse {
public String error;
#JsonDeserialize(as=ArrayList.class, contentAs=ListItem.class)
public List<ListItem> object;
}
Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).
I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:
public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
private final BeanProperty property;
/**
* Default constructor needed by Jackson to be able to call 'createContextual'.
* Beware, that the object created here will cause a NPE when used for deserializing!
*/
public JsonCollectionDeserializer() {
super(Collection.class);
this.property = null;
}
/**
* Constructor for the actual object to be used for deserializing.
*
* #param property this is the property/field which is to be serialized
*/
private JsonCollectionDeserializer(BeanProperty property) {
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new JsonCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
switch (jp.getCurrentToken()) {
case VALUE_STRING:
// value is a string but we want it to be something else: unescape the string and convert it
return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
default:
// continue as normal: find the correct deserializer for the type and call it
return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
}
}
}
Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.
In your example you would now have to annotate your collection field like so:
public class ListResponse {
public String error;
#JsonDeserialize(using = JsonCollectionDeserializer.class)
public ArrayList<ListItem> object;
public ListResponse() {}
}
And that should be it.
Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.
The register subTypes works!
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
}
public class Point implements Geometry{
private String type="Point";
....
}
public class Polygon implements Geometry{
private String type="Polygon";
....
}
public class LineString implements Geometry{
private String type="LineString";
....
}
GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
try {
geojson=mapper.readValue(source, GeoJson.class);
} catch (IOException e) {
e.printStackTrace();
}