Jackson JsonMappingException Error - Multiple fields representing property 'PROPERTY_NAME' - java

I have a class where I have multiple field named results, (Actually I have 12 of them, but for the sake of the question, I just include 2 in this question)
public class APIRequest {
#JsonProperty("code")
public String code;
#JsonProperty("error")
public APIError error;
#JsonProperty("results")
public APILogin login;
#JsonProperty("results")
public APIUser user;
}
The reason I have this because my backend API call will always return the results field for every request
for example http://api.testapp.com/get_user_profile would return this JSON
The results key would then be mapped by APIUser class
{
"code": "200",
"results": {
"name": "Jackson Liu"
"age": "21"
"first_name": "Jackson"
"last_name": "Liu"
}
}
And then http://api.testapp.com/login would return this JSON
The results key would then be mapped by APILogin class
{
"code": "200",
"results": {
"token": "12u3912edsdnisnknaklsmdlsadmsalma"
"session_id": "ladlwjopwjwpdmdlndlkadlaasassa"
"state": "1"
}
}
And because of that, I will get this error.
com.fasterxml.jackson.databind.JsonMappingException: Multiple fields
representing property "results":
id.testapp.android.testapp.jsonobjects.APIResults#login vs
id.testapp.android.testapp.jsonobjects.APIResults#user
Any thoughts on how should I fix this?

To make it simple use MAP. Jackson will take care of populating MAP. Just provide setter and getter for each field. And Depends on your context you can read required field in Map
public class APIRequest {
#JsonProperty("code")
public String code;
#JsonProperty("error")
public APIError error;
#JsonProperty("results")
Map<String, String> results;
}

Related

Jersey Rest Request Generic type

I am having the trouble to sent the generic json request from client to server. Here is my service and pojo classes. Can some correct me where i am doing wrong.
Service :
#POST
#Path("/executeApp")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response executeApp(Configuration configuration) {
}
Config class: This class consist list of Tasks,Task is abstract class is below.
#XmlRootElement
public class Configuration{
public Configuration() {
// TODO Auto-generated constructor stub
}
public String udid;
public boolean overrideExistingTask;
public int repeatPeriodMinutes;
public List<? extends Task> tasks;
//Getters and setters
}
Task class:
public abstract class Task {
public Task(){
}
public String testId;
public int pauseSeconds;
public int cycles;-
public abstract String getFriendlyName();
protected abstract Task<?> getTaskInstance();
//Getters and setters
}
Derived classes XXXX,YYYYY both extends Task:
public class XXXXX extends Task{
String friendlyName = this.getClass().getSimpleName();
//Extra varibles
public XXXXX() {
super();
}
#Override
public String getFriendlyName() {
return friendlyName;
}
#Override
protected Task<?> getTaskInstance() {
}
}
Client postman request with json:
{
"udid":"123",
"overrideExistingTask":"true",
"repeatPeriodMinutes":"2",
"tasks":[{"XXXXX":{"testId":"testId", "pauseSeconds" :"5", "cycles":"2" ,"requestId":"123" ,"url":"url", "port":"port" , "udid":"udid" }}]
}
error:
javax.ws.rs.ProcessingException: Error deserializing object from entity stream.</p>
The server encountered an unexpected condition that prevented it from fulfilling the request.
javax.servlet.ServletException: javax.ws.rs.ProcessingException: Error deserializing object from entity stream.
If i pass empty array instead of XXXXX, I am able to see the values of Configuration class(udid,overrideExistingTask,repeatPeriodMinutes) in debug mode but taskes values coming []. User can pass list of XXXXX ,YYYYY or both. Can some one suggest me where a doing wrong or missing rest annotation to define generic type.
The Configuration object will not be able to parse the provided JSON input.
The tasks fields in the Configuration is a list of tasks. In the JSON provided, it is a List of Map(/Object) with key "XXXXX" and value is the Task Object.
If this is the JSON to be used, then the tasks field in the Configuration object should be modified to
public List<Map<String,<? extends Task>> tasks;
or
The JSON input should be modified to send a list of tasks.
{
"udid": "123",
"overrideExistingTask": "true",
"repeatPeriodMinutes": "2",
"tasks": [{
"testId": "testId",
"pauseSeconds": "5",
"cycles": "2",
"requestId": "123",
"url": "url",
"port": "port",
"udid": "udid"
}
//More task objects here
]
}

Custom JSON serialization for Audit Infos with Spring Data Repositories

I'm implementing a company internal REST service using spring boot 1.5.2 with Spring Data JPA and Data Rest.
Problem
I'm looking for an efficient way to serialize objects as strings when exposing certain domain models using Spring Data Rest-Repositories.
Context
My domain models all extend from BaseEntity which looks like this:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
#Version
private Long version;
}
With this, each domain model has the properties createdBy, createDate, lastModifiedBy and lastModifiedDate exposed as shown in this example entity:
public class TestEntity extends BaseEntity { private String name; }
The corresponding JSON output looks like this:
{
"createdBy":
{
"name": "testEM",
"contactInfo":
{
"title": null,
"givenName": "GivenName",
"surName": "Surname",
"mail": "test#test.mail.de"
},
"function": "EMPLOYEE",
"department":
{
"name": "mydep"
}
},
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedBy":
{
<same representation as "createdBy">
},
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"name": "Hello,Name!",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/testres/1"
},
"testEntity":
{
"href": "http://localhost:8080/testres/1{?projection}",
"templated": true
}
}
}
What I want
Now I'd like to achieve a shorter representation of createdBy and lastModfifiedBy so that these entries don't contain the User object. Instead only the name (from User.getName()) should be displayed:
{
"createdBy": "testEM",
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedBy": "testEM",
"lastModifiedDate": "2017-06-12T11:49:17.013Z",
... // other properties
}
What is the best way to achieve this?
I've tried:
using #JsonIdentityInfo on the User entity -- This one didn't have any effect at all
registering custom (de)serializers for the User entity via #Bean Jackson2ObjectMapperBuilderCustomizer customizer() {...} -- Rendered { "createdBy": { "content": "testEM"}}
annotating the overridden method public User getCreatedBy() in my BaseEntity class with #JsonSerialize(using= UserJsonSerializer.class) -- this one throws an exception
{
"timestamp": 1497515751192,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.http.converter.HttpMessageNotWritableException",
"message": "Could not write content: Can not override serializer; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not override serializer",
"path": "/testres/1"
}
I've also read about using #JsonView, however, it didn't become clear to me how to enable these for the given use case
Update
I've created some projections, which is the out-of-the-box supported way to reduce output. See this Gist for the code I've written.
With these in place, and the projections set as excerpts, the list of entries is displayed fine. However, when you request a specific resource like localhost:8080/testRepo/1 you get the unprojected output. I know that Spring won't apply projections to specific entities per default. So we'd have to apply the request parameter ?=projection=testProjection to each request.
Since this is doable (because the app won't be public) it may be okay, but for others it may not. So the questions still stands, how can we alter the audit info in an efficient way for each resource?
Update 2
I've read again the Spring Data REST Documentation and stumbled upon this paragraph:
There is another route. If the Address domain object does not have it’s own repository definition, Spring Data REST will inline the data fields right inside the Person resource.
So you have to expose an UserRepository when the auditor is of type User.
Coincidently, this is the exact behaviour which I experienced when creating a MWE (minimal working example, can't upload to github, since I'm behind a proxy :( ).
So, with a #RepositoryRestResource UserRepository extends JpaRepository<User, Long> publicly exposed, Spring generates this JSON:
{
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"name": "Hello,EM!",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/testRepo/1"
},
"testEntity":
{
"href": "http://localhost:8080/testRepo/1{?projection}",
"templated": true
},
"lastModifiedBy":
{
"href": "http://localhost:8080/testRepo/1/lastModifiedBy"
},
"createdBy":
{
"href": "http://localhost:8080/testRepo/1/createdBy"
}
}
}
This behaviour is acceptable for me, so consider this question solved.
If anyone has additional input feel free to post!
Any help here is much appreciated!
This isn't a solution for my asked question, but it is an acceptable compromise for me and the company.
Quick solution:
When you expose an RestRepository<User> in your API and your auditor is of the same type User, Spring will generate HAL-links to createdBy and lastModifiedBy. Both audit dates will be inlined still since they are simple strings (due to the JodaTime conversion).
Example code:
// resolves auditor from SecurityContext
public class AuditorAwareImpl implements AuditorAware<User> {
#Override
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof WrappedUser) {
WrappedUser principal = (WrappedUser)authentication.getPrincipal();
return principal.getUser();
}
throw new IllegalStateException("No current auditor available!");
}
}
Expose the UserRepository:
//exported is true by default
#RepositoryRestResource(exported = true)
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByName(String loginName);
}
Create AuditEntity from which all other domain objects inherit:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
#javax.persistence.Version
private Long version;
}
Expose your domain models:
#Entity
public class Project extends BaseEntity {
private String project_name;
// other properties
}
#RepositoryRestResource
public interface ProjectRepo extends JpaRepository<User, Long> {}
This will generate following JSON for /projects/{id}:
{
"createdDate": "2017-06-12T11:49:17.013Z",
"lastModifiedDate": "2017-06-14T11:27:32.370Z",
"project_name": "MyExampleProjectName",
"new": false,
"_links":
{
"self":
{
"href": "http://localhost:8080/projects/1"
},
"project":
{
"href": "http://localhost:8080/projects/1{?projection}",
"templated": true
},
"lastModifiedBy":
{
"href": "http://localhost:8080/projects/1/lastModifiedBy"
},
"createdBy":
{
"href": "http://localhost:8080/projects/1/createdBy"
}
}
}

How to make POJO dynamic so that it ignores an json tag but reads the value under that tag using jackson in java?

I've a parent DAO:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
Child DAO:
#XmlAccessorType(XmlAccessType.FIELD)
public class ListWrapper {
#XmlElementWrapper(name = "attrValue")
private List<Object> list;
JSON request that works (if I use "metadata" name as root element):
"metadata": {
"nameValueForListValue": {
"signed": {
"attrValue": [
"ahsdfhgakjf"
]
},
"id": {
"attrValue": [
"12345678",
"87654321"
]
},
.......... continues
I don't want the tag "nameValueForListValue" in request, instead it should be smart enough to read rest of the values without that tag. Looks like it always needs to have the param name "nameValueForListValue" on the request. Is there any annotations that will do my job easier? I'm using Java 6 & jackson 1.9.
What about using #JsonAnySetter Jackson annotation
It would be something like:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
#JsonAnySetter
public void genericSetter(String key, Object value){
nameValueForListValue.put(key, value);
}
}
That whay any unknown field could be handle by this setter.
More info:#JsonAnySetter example
#JsonInclude(JsonInclude.Include.NON_NULL)

Jackson deserialization: Custom Object factory

How do you implement in Jackson a conversion from json to Java objects, based on class types specified in the json.
Example Java Types:
public class Car{
public String make;
public String model;
}
public class Spoon {
public String material;
}
public class Ownership {
public List<Object> items;
public User owner;
}
Example Json:
{
"items": [
{
"#class": "com.example.Car",
"make": "Mercedes-Benz",
"model": "S500"
},
{
"#class": "com.example.Spoon",
"material": "silver"
}
],
"owner": {
"name": "John"
}
}
Since the number of classes is unknown (users can add any class) it is not possible to use the annotation #JsonSubTypes.
In addition, the json may contain known strongly types classes, like the object User in the example which is serialized using the standard Jackson implementation.
Most of the examples I can find, such as http://www.baeldung.com/jackson-inheritance assume the number of subclasses is known, but in my case it is not, users of the framework will add their own.
Ideally the implementation will just resolve types and let Jackson do the rest of the serialization without repeating that code.
Can be solved using an annotation on the collection:
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "#class")
public List<Object> items;

Jackson Jersey JSON

I'm trying to use Jersey and Jackson (although any other way of doing JSON demarshalling works) to get this into my system in some form (be it POJO or some other representation).
Basically I only need the data section. I was trying to use GenericTypes with lists, but this is a nested list and I'm just not sure what to do. Lots of kudos for help and I really appreciate it!
{
"total": 4,
"data": [
{
"descriptor": "",
"multiInstance": false,
"active": false
},
{
"descriptor": "Apparel",
"multiInstance": true,
},
{
"descriptor": "abcd123",
"multiInstance": false,
},
{
"descriptor": "abcd",
"multiInstance": false,
}
]
}
This is the class I'm trying to use. I need a list of the class.
public class customObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
//getters and setters
}
Edit:
I'm using it in here.
CustomObjectResponse WDCOResponse =
resource
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", getToken()).get(WDCOResponse.class);
But it's still not working.
Edit2:
Figured this out! Thanks to everyone. :)
I had to add annotation to tell it to ignore if something wasn't found, some of the JSON I'm getting back was not fully-formed in that not all fields were absolutely neccesary.
If you the object you provided is what you are passing to your controller, then you will need one more wrapper object to contain the list like this:
public class CustomRequest {
#JSonProperty(value = "total");
private Integer total;
#JsonProperty(value = "data")
private List<CustomObject> data;
// getters/setters
}
public class CustomObject {
#JsonProperty(value = "descriptor")
private String descriptor;
#JsonProperty(value = "multiInstance")
private Boolean multiInstance;
// getters/setters
}
Then your controller will just have annotations that show that the RequestBody is the CustomRequest class:
#Controller
public class JSONController {
#RequestMapping(value="sendData")
public #ResponseBody CustomResponse sendData(
#RequestBody CustomRequest request)
{
return null;
}
}
If you are still getting errors, please provide detailed error or problem. Thanks!
You'd use POJO like:
public class Response {
int count;
List<customObject> data;
}
and access the data from there:
for (customObject ob : response.data) {
// process ig
}

Categories