I made a code which add comments on my localhost:3000 but its parsing to much info i want to remove "commentModel" but if i remove it from CommentRq class i get errors
comment example:
{ "commentModel": { "comment": "komentarz", "date": "3/6/19 9:34 AM" }, "id": 1}
i want it to be { "comment": "komentarz", "date": "3/6/19 9:34 AM" }, "id": 1 }
CommentRq
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class CommentRq {
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private CommentModel commentModel;
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public static class CommentModel {
#JsonProperty("comment")
String resourceName;
#JsonProperty("date")
String resourceNamed;
}
}
CommentBody
public class CommentBody {
Date now = new Date();
#JsonInclude(JsonInclude.Include.NON_NULL)
public CommentRq RequestCommentBody() {
return CommentRq.builder()
.commentModel(new CommentRq.CommentModel(
"komentarz",
(DateFormat.getInstance().format(now))
))
.build();
}
}
Here i create comment
Interface.PostComment postComment = Feign.builder()
.client(new OkHttpClient())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.logger(new Slf4jLogger(Interface.PostComment.class))
.logLevel(Logger.Level.FULL)
.target(Interface.PostComment.class, "http://localhost:3000/comments/");
#When("i try to add a comment")
public void postComment() {
Map<String, Object> headermap = new HashMap<>();
headermap.put("Content-Type", "application/json");
CommentBody requestComment = new CommentBody();
CommentRes commentRes = postComment.postComment(headermap, requestComment.RequestCommentBody());
id = commentRes.getId();
LOGGER.info("Created: " + DateFormat.getInstance().format(now));
}
You can annotate your private CommentModel commentModel with #JsonUnwrapped. It will unwrap your commentModel object and write its fields to the root of the json. This will handle your specific case. But you can revise your request structure as well: put CommentModel fields into CommentRq and map CommentModel object to CommentRq object.
Related
I have been wondering what exactly I am doing wrong here. The response I am getting from my POJO class has a root property that I am unable to remove.
I have this JSON response:
{
"page": 1,
"per_page": 6,
"total": 12,
"total_pages": 2,
"data": [
{
"id": 1,
"name": "cerulean",
"year": 2000,
"color": "#98B2D1",
"pantone_value": "15-4020"
},
{
"id": 2,
"name": "fuchsia rose",
"year": 2001,
"color": "#C74375",
"pantone_value": "17-2031"
}
],
"support": {
"url": "https://reqres.in/#support-heading",
"text": "To keep ReqRes free, contributions towards server costs are appreciated!"
}
}
I converted JSON to these POJO classes and ignore properties not required for my test.
First POJO
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Datum{
public int id;
public String name;
public int year;
public String color;
public String pantone_value;
}
Second POJO
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Root {
#JsonIgnore
public int page;
#JsonIgnore
public int per_page;
#JsonIgnore
public int total;
#JsonIgnore
public int total_pages;
public ArrayList<Datum> data;
#JsonIgnore
public Support support;
}
Third POJO:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Support {
public String url;
public String text;
}
I want to get the properties in the Responses' Data list and convert it to a map, so I did this:
public void verify( List<Map<String, String>> myTest) { //myTest holds the expected response i want to use for my assertion
Root response = (resp.as(Root.class));
Map<String, Object> mapResponse = mapper.convertValue(response, new TypeReference<>() {
});
System.out.println(mapResponse);
}
Output:
{data=[{id=1, name=cerulean, year=2000, color=#98B2D1, pantone_value=15-4020}, {id=2, name=fuchsia rose, year=2001, color=#C74375, pantone_value=17-2031}, {id=3, name=true red, year=2002, color=#BF1932, pantone_value=19-1664}]}
The {data= root property (key) at beginning of the output is what I was trying to remove as it's making my assertion fail.
This is the output I would like:
[{id=1, name=cerulean, year=2000, color=#98B2D1, pantone_value=15-4020}, {id=2, name=fuchsia rose, year=2001, color=#C74375, pantone_value=17-2031}]
How can I convert the response's data format to get this?
You can convert only data list
List<Map<String, Object>> mapResponse = mapper.convertValue(response.getData(), new TypeReference<>() {
});
System.out.println(mapResponse);
I'm trying to send a HTTP request to an API which accepts a JSON request body in this format
{
"firstName" : "XYZ",
"family" : {
"commonDetails" : {
"secondName" : "ABC"
},
"1" : "Mother name",
"2" : "Father name",
"3" : "Spouse name"
}
}
So I have defined a request payload the below way.
public class UserDetails {
private String firstName;
private Map<String, AbstractFamilyDetails> details;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties("type")
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = FamilyCommonDetails.class, name = "COMMON_DETAILS"),
#JsonSubTypes.Type(value = FamilyIndividual.class, name = "FAMILY")
})
public abstract class AbstractFamilyDetails {
private String type;
}
#Data
#EqualsAndHashCode(callSuper = true)
public class FamilyCommonDetails extends AbstractFamilyDetails {
private String secondName;
#Builder
public FamilyCommonDetails(String secondName) {
super("COMMON_DETAILS");
this.secondName = secondName;
}
public FamilyCommonDetails() {
super("COMMON_DETAILS");
}
#Override
public String toString() {
return this.secondName;
}
}
#Data
#EqualsAndHashCode(callSuper = true)
public class FamilyIndividual extends AbstractFamilyDetails {
private String individual;
#Builder
public FamilyIndividual(String individual) {
super("FAMILY");
this.individual = individual;
}
public FamilyIndividual() {
super("FAMILY");
}
#Override
public String toString() {
return this.individual;
}
}
But when I debug what json payload I'm hitting the server with turns out to be
{
"firstName" : "XYZ",
"family" : {
"commonDetails" : {
"secondName" : "ABC",
},
"1" : {"individual": "Mother name"},
"2" : {"individual": "Father name"},
"3" : {"individual": "Spouse name"}
}
}
Where is it that I'm going wrong? Do I have to define a custom jackson deserialiser to achieve this?
Try the #JsonUnwrapped annotation on the individual field.
See: https://fasterxml.github.io/jackson-annotations/javadoc/2.8/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Have tried using #JsonUnwrapped as #user3296624 suggested. However it didn't work. There is an open issue on jackson-databind related to the same. Issue
Have tried #JsonAnySetter too, which did't help.
So, modified my request model to private Map<String, Object> details and discarded AbstractFamilyDetails as the last resort and used it accordingly. Not an ideal solution. But worked.
I have this JSON: (passed as Map<String, Object>)
{
"id": 1000,
"lab": [
"LAB1",
"LAB2",
"LAB3"
],
"name": "TEST",
"ref": {
"id": 1000,
"code": "REFCODE",
"description": "REF DESC"
},
"employee": {
"id": 1000,
"name": "Emp1000",
"tin": null,
"active": true
},
"contacts": [
{
"id": 1000,
"name": "Contact 1",
"emailAddress": "contact1#test.com",
"active": true,
"positions": [
{
"position": {
"id": 1000,
"code": "POS",
"description": "POS DESC"
}
}
]
}
],
"status": "NEW"
}
This is my DTO and ContactDTO:
public class DTO {
private Long id;
...
#JsonProperty("contacts")
private List<ContactDTO> contacts;
}
#Builder
public class ContactDTO implements Serializable {
private Long id;
private String name;
private String emailAddress;
private Boolean active;
#NotEmpty
private List<ContactPositionDTO> positions;
}
Here is my service class with object mapper and process method which accepts the JSON map:
private ObjectMapper objectMapper() {
var objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
return objectMapper;
}
public void process(final Map<String, Object> map) {
objectMapper().convertValue(map, DTO.class);
}
However, I am getting java.lang.IllegalArgumentException: Cannot deserialize value of type java.util.ArrayList
And if I add DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY I am getting a different error:
java.lang.IllegalArgumentException: Cannot construct instance of ContactDTO (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('[{id=1000, name=Contact 1, .....
You have two alternative options to fix your ContactDTO class:
Add a no-arguments constructor
public ContactDTO() { }
to the class. To fix the then upcoming compiler error
you will need to remove the #Builder annotation.
Keep the #Builder annotation
and add the #Jacksonized annotation to the class.
This will configure the generated builder to cooperate
with Jackson's deserialization.
For more details see Lombok's documentation about #Jacksonized.
To deserialize you need a No arg constructor and to use #Builder you need an all arg constructor.
So you need tu add both.
The example below should work. I just added #Getter annotation to avoid using #JsonProperty
#Getter
public static class DTO {
private Long id;
#JsonProperty("contacts")
private List<ContactDTO> contacts;
}
#Builder
#Getter
#NoArgsConstructor
#AllArgsConstructor
public static class ContactDTO implements Serializable {
private Long id;
private String name;
private String emailAddress;
private Boolean active;
private List<ContactPositionDTO> positions;
}
#Getter
public static class ContactPositionDTO {
private Position position;
#JsonProperty("effectiveStartDate")
private List<Integer> date;
#Getter
static class Position {
private Integer id;
private String code;
private String description;
}
}
NB: you can also use #Jacksonized annotation instead of #NoArgsConstructor and #AllArgsConstructor
How can I map a flat map to a nested pojo?
I've tried using this, however I get an unrecognized field exception on the field sword.
Map<String, Object> values = ...;
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(mapper.writeValueAsString(values), Person.class);
I have this dummy json:
{
"firstName": "Arya",
"lastName": "Stark",
"gender": "Female",
"sword" : "Excalibur",
"shield": "Mighty Shield"
}
a person class:
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
private Equipment equipments;
}
and an equipment class:
#Data
#AllArgsConstructor
public class Equipment {
private String sword;
private String shield;
}
I see this way to solve this problem:
public class Test {
public static void main(String[] args) {
Map<String, Object> values = new HashMap<>();
values.put("firstName", "Arya");
values.put("lastName", "Stark");
values.put("gender", "Female");
values.put("sword", "Excalibur");
values.put("shield", "Mighty Shield");
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Person person = mapper.convertValue(values, Person.class);
person.setEquipments(mapper.convertValue(values, Equipment.class));
System.out.println(person);
}
}
Of course it is not one line deserialization but it works. Result:
Person(firstName=Arya, lastName=Stark, equipments=Equipment(sword=Excalibur, shield=Mighty Shield))
Note: add #NoArgsConstructor to your POJO, it is required by Jackson.
Meanwhile jackson added #JsonUnwrapped and lombok added #Jacksonized this reduces your overhead to a minimum of:
#Value
#Builder
#Jacksonized
public class Person {
private String firstName;
private String lastName;
#JsonUnwrapped
private Equipment equipments;
}
Note: #Jacksonized is only necessary in combination with #Builder. But this prevents us from using the #NoArgsConstructor
I think you have a mistake when you collect attributes for different classes into one map. Why did you do that?
It's easy transform json to object with nested classes if your json has the same structure. In your example it should be:
{
"person": {
"firstName": "name",
"lastName": "name2",
"gender": "male",
"equipment": {
"sword": "s",
"shield": "s2"
}
}
}
That json can be easily transformed to Person.java by objectMapper.readValue(json, Person.class);
I have one object contains a HashMap[string, string], looks like:
#Data
#Document(indexName = "customer", type = "customer")
public class Customer {
#Id
private String id;
private String name;
private Map<String, String> tags;
}
Then I have save some item in elasticsearch like this:
customer1:
{
"name": "n1",
"tag": {
"host": "hostA"
}
}
customer2:
{
"name": "n1",
"tag": {
"host": "hostB"
}
}
I want to query the first one which tagValue is "hostA"
My query syntax like this:
#Service
public class CustomerService {
#Autowired
private ElasticsearchTemplate template;
public void find() {
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(new BoolQueryBuilder()
.must(matchQuery("tags.host", "hostA")))
.build();
List<Customer> customers = template.queryForList(query, Customer.class);
customers.forEach(customer -> System.out.println(customer + "\n"));
System.out.println(customers.size());
}
}
The BooleanQueryBuilder looks very stupid:
new BoolQueryBuilder().must(matchQuery("tags.host", "hostA"))
Is there any clear syntax like this
#Repository
public interface CustomerRepository extends ElasticsearchRepository<Customer, String> {
List<Customer> findByTags(Map<String, String> tags);
}