Jersey JSON parsing with root name of class name - java

I have a pojo class.
#XmlRootElement(name = "project")
public class Project {
private UUID id;
private String label;
private String name;
//getters and setters
}
It accepts this as input if I try from postman.
{
"label" : "label",
"name" : "name"
}
But, I want it to accept values as
{
"project" : {
"label" : "label",
"name" : "name"
}
}
My endpoint method is
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createProject(Project project) {
log.info("createProject called." + project);
if (project == null) {
return Response.ok(false).build();
}
ps = new ProjectServiceImpl();
return Response.ok(ps.createProject(project)).build();
}
There are various other methods which are like this, like get all projects, where other party expects 'root-name' or 'class-name' like this at the start of json. I am stuck with things like this and it's slowing down my work. Please suggest me something or provide any source where I can read the stuff.

I think SerializationFeature.WRAP_ROOT_VALUE and DeserializationFeature.UNWRAP_ROOT_VALUE are configuration options that you are looking for.
Read more here:
https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features
https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

Please use #XmlRootElement instead of #XmlRootElement(name = "project") and try it out

A good read if you are using spring and consuming a rest service - http://spring.io/guides/gs/consuming-rest/

Related

How to aggregate in spring data mongo db a nested object and avoid a PropertyReferenceException?

I am creating a new endpoint in springboot that will return simple stats on users generated from an aggregate query in a mongo database. However I get a PropertyReferenceException. I have read multiple stackoverflow questions about it, but didn't find one that solved this problem.
We have a mongo data scheme like this:
{
"_id" : ObjectId("5d795993288c3831c8dffe60"),
"user" : "000001",
"name" : "test",
"attributes" : {
"brand" : "Chrome",
"language" : "English" }
}
The database is filled with multiple users and we want using Springboot aggregate the stats of users per brand. There could be any number of attributes in the attributes object.
Here is the aggregation we are doing
Aggregation agg = newAggregation(
group("attributes.brand").count().as("number"),
project("number").and("type").previousOperation()
);
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, Profile.class, Stats.class);
return groupResults.getMappedResults();
Which produces this mongo query which works:
> db.collection.aggregate([
{ "$group" : { "_id" : "$attributes.brand" , "number" : { "$sum" : 1}}} ,
{ "$project" : { "number" : 1 , "_id" : 0 , "type" : "$_id"}} ])
{ "number" : 4, "type" : "Chrome" }
{ "number" : 2, "type" : "Firefox" }
However when running a simple integration test we get this error:
org.springframework.data.mapping.PropertyReferenceException: No property brand found for type String! Traversed path: Profile.attributes.
From what I understand, it seems that since attributes is a Map<String, String> there might be a schematic problem. And in the mean time I can't modify the Profile object.
Is there something I am missing in the aggregation, or anything I could change in my Stats object?
For reference, here are the data models we're using, to work with JSON and jackson.
The Stats data model:
#Document
public class Stats {
#JsonProperty
private String type;
#JsonProperty
private int number;
public Stats() {}
/* ... */
}
The Profile data model:
#Document
public class Profiles {
#NotNull
#JsonProperty
private String user;
#NotNull
#JsonProperty
private String name;
#JsonProperty
private Map<String, String> attributes = new HashMap<>();
public Stats() {}
/* ... */
}
I found a solution, which was a combination of two problems:
The PropertyReferenceException was indeed caused because attributes is a Map<String, String> which means there is no schemes for Mongo.
The error message No property brand found for type String! Traversed path: Profile.attributes. means that the Map object doesn't have a brand property in it.
In order to fix that without touching my orginal Profile class, I had to create a new custom class which would map the attributes to an attributes object having the properties I want to aggreate on like:
public class StatsAttributes {
#JsonProperty
private String brand;
#JsonProperty
private String language;
public StatsAttributes() {}
/* ... */
}
Then I created a custom StatsProfile which would leverage my StatsAttributes and would be similar to the the original Profile object without modifying it.
#Document
public class StatsProfile {
#JsonProperty
private String user;
#JsonProperty
private StatsAttributes attributes;
public StatsProfile() {}
/* ... */
}
With that I made disapear my problem with the PropertyReferenceException using my new class StatsAggregation in the aggregation:
AggregationResults<Stats> groupResults
= mongoTemplate.aggregate(agg, StatsProfile.class, Stats.class);
However I would not get any results. It seems the query would not find any document in the database. That's where I realied that production mongo objects had the field "_class: com.company.dao.model.Profile" which was tied to the Profile object.
After some research, for the new StatsProfile to work it would need to be a #TypeAlias("Profile"). After looking around, I found that I also needed to precise a collection name which would lead to:
#Document(collection = "profile")
#TypeAlias("Profile")
public class StatsProfile {
/* ... */
}
And with all that, finally it worked!
I suppose that's not the prettiest solution, I wish I would not need to create a new Profile object and just consider the attributes as a StatsAttributes.class somehow in the mongoTemplate query. If anyone knows how to, please share 🙏

Java Json serialization of Interface types

I have a json webservice I'm calling with a spring rest template. One of the request parameters is an interface. I'm looking for the right combination of annotations (either Jackson or Jaxb) that will create my json request.
My request needs to look like this:
{
"request": {
"specificAccountIdentifier": {
"field1" : "value",
"field2" : "value"
}
}
}
However, right now, it's marshaling as this:
{
"request": {
"accountIdentifier": {
"field1" : "value",
"field2" : "value"
}
}
}
Request Class:
#XmlRootElement
#XmlType
#XmlAccessorType(XmlAccessType.FIELD)
public class Request {
#XmlAnyElement
#XmlElementRefs({#XmlElementRef(type = SpecificAccountIdentifier.class)})
private AccountIdentifier accountIdentifier;
public Request() {
}
}
AccountIdentifier:
#XmlSeeAlso(SpecificAccountIdentifier.class)
public interface AccountIdentifier extends Serializable {
}
SpecificAccountIdentifier:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
#XmlType
public class SpecificAccountIdentifier implements AccountIdentifier {
private static final long serialVersionUID = 8894475816041559L;
private Long field1;
private Long field2;
...class details...
}
I've tried a few different combinations of #JsonTypeInfo, but can't get anything to work right.
UPDATE:
Even after looking into the answer below, I still could not get anything to work, so I ended up just writing my own custom Serializer.
Found similar question asked 4 years ago but it seems you can use it to kick start the idea behind.
Basically it is using EclipseLink JAXB (MOXy) and a factory method to generate the Implementation of your interface.
https://stackoverflow.com/a/16155520/6039974

Json Deserialization - Mapping nested object keys directly to class

I have a json like -
{
"type" : "employee",
"details" : {
"name" : "ABC",
"age" : 12,
"sex" : "male"
}
}
And a Java Class like -
public class Person {
String name;
String sex;
String type;
int age;
----getters and setters
}
I was wondering is there a ways to directly map the attributes of the details object to the person class like details.name to Person.name.
I know this can be achieved with custom deserializers, but I was hoping to avoid it. May be some annotations that GSON or Jackson provides.
There are a few ways to solve this, but what I would do is create the following class:
public class PersonWrapper {
private String type;
#JsonProperty("details")
private Person person;
}
EDIT:
If you don't want to add a wrapper class, you can try adding #JsonRootName(value = "details") to your Person class.
you can use #JsonProperties for mapping

How to ignore(/hide) attributes of class in JSON response

I am new to Java RESTful web services. I have classes structure as following:
class BaseClass{
String basicInfo;
}
class DataClass extends BaseClass{
String id;
}
class DataList extends BaseClass{
List<DataClass> dataList;
}
when I get DataList class as Response of web-service, I get it in following format:
{
"basicInfo" : "Mandetory",
"dataList" : [
{
"basicInfo" : "Optional",
"id" : "I_1001"
},
{
"basicInfo" : "Mandetory",
"id" : "I_1002"
}
]
}
But I want to ignore "basicInfo" attribute in every Data Object of dataList.
ex.
{
"basicInfo" : "Mandetory",
"dataList" : [
{
"id" : "I_1001"
},
{
"id" : "I_1002"
}
]
}
Is there any way through which I could ignore these attributes(using annotations)?
Note : I can't change the class structures.
#JsonIgnore
the above is the annotation you require. for more variations of this you can go through the following link. add the above annotation to the fields which you wish to ignore in your transfer object and it will be ignore when parsing. Since you haven't mentioned which library you are using to work with json, i have answered your question using Jackson Library.
You can use JAX-B annotation #XmlTransient on your attribute basicInfo.
Alternatively, when JSON-B (JSR 367) will be available (Java EE 8), you can use the #JsonbTransient or, even better, the #JsonbVisibility attribute.

Default Property Naming Strategy in dropwizard

In my dropwizard project I have bean classes, which are used in the Resource class like this :
#GET
#Path("/{id}")
public Response getUser(#PathParam("id") Long id) {
return Response.ok(userDAO.get(id)).build();
}
class User {
private String id;
#JsonProperty("first_name")
private String firstName;
#JsonProperty("last_name")
private String lastName;
}
Is there a way that I can add to dropwizard configuration or in the application class to tell javax to map my bean entity (user) to json using snake_case naming strategy. This will help me avoid using the #JsonProperty("first_name") annotation for every member of the class.
In the absence of the aforementioned annotation, my json looks like this :
{
"firstName": "John",
"lastName": "Doe"
}
I would rather like it to be :
{
"first_name": "John",
"last_name": "Doe"
}
Found the answer on another SO question :
How to make #JsonSnakeCase the default for configuration in Dropwizard
Adding the following line to application does the job !
environment.getObjectMapperFactory()
.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
You can annotate the whole entity with #JsonSnakeCase to avoid annotating each and every property.
Otherwise you can configure the global ObjectMapper in your application initialization
objectMapper.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

Categories

Resources