I am creating program "Ad catalog" that executes CRUD operations. I need to run method save(Ad ad), Ad includes parameter local date. I put JSON code through Restlet to get a result but it gives me error 500 (wrong local date). How do I put local date to be correct?
I tried to write it like "date": "2012-03-19T07:22Z", "date": "2019-09-19", "date": 2019-09-19 and don't put it at all but it didn't work.
#Entity
public class Ad {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ad_id")
private int id;
private String name;
private LocalDate date;
private String text;
#Column(name = "price", precision = 10, scale = 2)
private BigDecimal price;
#ManyToOne
#JoinColumn(name = "author_fk_id")
private Author author;
#ManyToOne
#JoinColumn(name = "category_fk_id")
private Category category;
.....
}
#RestController
#RequestMapping("ad")
public class AdController {
#Autowired
#Qualifier("adServiceImpl")
private AdService<Ad> adService;
#PostMapping("/save")
public void save(#RequestBody Ad ad) {
adService.save(ad);
}
}
JSON
{
"id": 0,
"name": "house for sale",
"text": "selling a house in barry ave, 5 ears old",
"date": 2019-09-20,
"price": 250.00,
"author": {
"id": 0,
"name": "Liza",
"phone": {
"id": 0,
"number": "3121001111"
},
"address": {
"id": 0,
"country": "RUSSIA",
"city": "MOSCOW"
},
"email": {
"id": 0,
"email": "liza#mail.ru"
}
},
"category": {
"id": 0,
"name": "houses"
}
}
Instead of using private LocalDate date; use the java.sql.date type and pass the date in json like this "date": 2019-09-20,
it will work and if you want to convert that date formate to other local date formate use the SimpleDateFormat and parse it.
Related
I have just started using Spring boot and I am using the default repository api to retrieve db data as json.
I have added a #ManyToOne relation to my Song and Artist Entity.
But now i am not getting the Artist object in my json response from the server and its not really clear to me, how I can include it without missing out on the pagination functions from the PagingAndSorting repository.
I am using the spring-data-rest-jpa.
My response now looks like:
"_embedded": {
"songs": [
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"_links": {
"self": {
"href": "http://localhost:8080/api/songs/1"
},
"song": {
"href": "http://localhost:8080/api/songs/1"
},
"artist": {
"href": "http://localhost:8080/api/songs/1/artist"
}
}
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/songs?page=0&size=1"
},
"self": {
"href": "http://localhost:8080/api/songs?size=1"
},
"next": {
"href": "http://localhost:8080/api/songs?page=1&size=1"
},
"last": {
"href": "http://localhost:8080/api/songs?page=19&size=1"
},
"profile": {
"href": "http://localhost:8080/api/profile/songs"
}
},
"page": {
"size": 1,
"totalElements": 20,
"totalPages": 20,
"number": 0
}
}
But I want it rather to be like this:
"_embedded": {
"songs": [
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"artist": {
"id": 1,
"name": "Artistname"
}
"_links": {
"self": {
"href": "http://localhost:8080/api/songs/1"
},
"song": {
"href": "http://localhost:8080/api/songs/1"
},
"artist": {
"href": "http://localhost:8080/api/songs/1/artist"
}
}
}
]
},
"_links": {
"first": {
"href": "http://localhost:8080/api/songs?page=0&size=1"
},
"self": {
"href": "http://localhost:8080/api/songs?size=1"
},
"next": {
"href": "http://localhost:8080/api/songs?page=1&size=1"
},
"last": {
"href": "http://localhost:8080/api/songs?page=19&size=1"
},
"profile": {
"href": "http://localhost:8080/api/profile/songs"
}
},
"page": {
"size": 1,
"totalElements": 20,
"totalPages": 20,
"number": 0
}
}
Song.java
#Getter
#Setter
#Entity
#Table(name = "song")
public class Song {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true)
private Long id;
#NotNull
#NotBlank(message = "The song has to have a title")
private String title;
#NotNull
#NotBlank(message = "The song has to have a genre")
private String genre;
#NotNull
#Min(value = 1, message = "The song has to have a song length in seconds")
private int length;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "artist_id", referencedColumnName = "id")
private Artist artist;
/* #Version
private long version;*/
public Song() {
}
public Song(String title, Artist artist, String genre, int length) {
this.title = title;
this.artist = artist;
this.genre = genre;
this.length = length;
}
public void setArtist(Artist artist) {
this.artist = artist;
}
public Artist getArtist() {
return artist;
}
}
Artist.java
#Getter
#Setter
#Entity
#Table(name = "artist")
public class Artist {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#NotNull
#NotBlank(message = "The artist has to have a name")
private String name;
#JsonIgnore
#OneToMany(mappedBy = "artist")
private List<Song> songs;
public Artist() {
}
public Artist(String name) {
this.name = name;
}
For the sake of testing I wrote a method in my SongController:
#GetMapping
List<Song> getSongs() {
return songRepository.findAll();
}
The result includes the Artist object, but won't have any pagination to it. How could I include it?
Json Result:
[
{
"id": 1,
"title": "SongTitle",
"genre": "Rap",
"length": 500,
"artist": {
"id": 1,
"name": "ArtistName"
}
}
]
After all your useful suggestions I have found an answer:
I have changed the return type of my Method in my controller to Page and made use of the PageRequest Class which looks like this:
#GetMapping
public Page<Song> getSongs(#RequestParam(defaultValue = "0") int page, #RequestParam(defaultValue = "5") int size) {
PageRequest pr = PageRequest.of(page, size);
return songRepository.findAll(pr);
}
Also used some defaultvalues to avoid some exceptions ;)
Use either #JsonIdentityInfo or #JsonIgnore and remove #JsonBackReference. Here is the example with #JsonIgnore
public class Artist {
public Long id;
public String name;
public List<Song> songs;
}
public class Song {
public Long id;
public String title;
public String genre;
public int length;
#JsonIgnore
public Artist artist;
}
#JsonManagedReference or #JsonBackReference won't help in this case (read more on https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion):
(...) we can use the #JsonIgnore annotation to simply ignore one of the sides of the relationship, thus breaking the chain. [my addition: the chain of recurrent calls]
Here is the example with #JsonIdentityInfo:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Artist { ... }
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Song { ... }
I have the following Employee data in MongoDB
{
"_id": {
"$oid": "625f09bb1a96bf42ff4c4006"
},
"employeeId": 1234,
"email": "jason#acme.com",
"firstName": "Jason",
"lastName": "Stuart",
"currentCTC": 1201117.61,
"department": {
"$ref": "department",
"$id": {
"$oid": "625f09bb1a96bf42ff4c4005"
}
}
}
{
"_id": {
"$oid": "625f09bb1a96bf42ff4c4006"
},
"employeeId": 1235,
"email": "jasons#acme.com",
"firstName": "Jasons",
"lastName": "Stuarts",
"currentCTC": 1201117.61,
"department": {
"$ref": "department",
"$id": {
"$oid": "625f09bb1a96bf42ff4c4005"
}
}
}
My Spring #Document looks like this:
// Employee.java
#Data
#Document
public class Employee {
#Id
private String id;
private Long employeeId;
private String email;
private String firstName;
private String middleName;
private String lastName;
private Gender gender;
private double currentCTC;
#DBRef
private Department department;
}
// Department.java
#Document
#Data
public class Department {
#Id
private String id;
private String name;
}
Now, my requirement is to find the sum of salaries Department-wise.. I need the data to be in the following way:
[
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": "Engineering"
},
"cost": 31894773.01
},
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": "Marketing"
},
"cost": 4552325.25
}
]
I created an aggregate function like this in Spring Data:
public List<DepartmentCost> getDepartmentCosting() {
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$$ROOT").as("department");
Aggregation aggregation = Aggregation.newAggregation(groupByDepartment);
AggregationResults<DepartmentCost> results = mongoTemplate.aggregate(aggregation, "employee", DepartmentCost.class);
return results.getMappedResults();
}
And my expected DepartmentCost.java
#Data
#Document
public class DepartmentCost {
#DBRef
private Department department;
private double cost;
}
Now when I try this API out, I get the data correctly, but I do not get department name. It comes as null. I get a response like
[
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": null,
},
"cost": 2241117.6100000003
},
{
"department": {
"id": "625f09bb1a96bf42ff4c400a",
"name": null,
},
"cost": 14774021.43
},
{
"department": {
"id": "625f09bc1a96bf42ff4c4013",
"name": null,
},
"cost": 14879633.97
}
]
How can I get the department details expanded in my model? Please help..
After a couple of attempts, I figured it out. All I had to do was this:
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$department").as("department");
as opposed to:
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$$ROOT").as("department");
I'm having problems parsing JSON, this is the error:
out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<packagename....>` out of START_OBJECT token
And I know why it is happening I just don't know how to fix it. This JSON works:
{
"status_code": "SUCCESS",
"time": {
"date": "Mar 23, 2021 1:14:39 AM"
},
"info": [
{
"person": "2.2",
"role": "TEACHER"
},
{
"person": "2.3",
"role": "TEACHER"
}
]
}
This one does not:
{
"status_code": "SUCCESS",
"time": {
"date": "Mar 23, 2021 3:49:27 AM"
},
"info": {
"id": "1",
"person": [
{
"identifier": "John",
"role": "TEACHER"
},
{
"identifier": "Homer",
"role": "TEACHER"
},
{
"identifier": "Michael",
"role": "TEACHER"
},
{
"identifier": "Sarah",
"role": "TEACHER"
}
]
}
}
The problem seems to be the { character in front of the info field because with [ works. So this is the method I'm using to parse the JSON:
public Mono<PersonResponse> searchById(String id) {
return webClient.get().uri(id).retrieve().bodyToMono(PersonResponse.class);
}
Also tried:
public Mono<PersonResponse[]> searchById(String id) {
return webClient.get().uri(id).retrieve().bodyToMono(PersonResponse[].class);
}
Error right on line: 1, column: 1. Any suggestions of how to implement the method?
EDIT: Added classes.
PersonResponse:
public class PersonResponse implements Serializable{
private static final long serialVersionUID = 7506229887182440471L;
public String status_code;
public Timestamp time;
public List<PersonDetails> info;
public PersonResponse() {}
...getters / setters / toSting
PersonDetails:
public class PersonDetails implements Serializable{
private static final long serialVersionUID = 1294417456651475410L;
private int id;
private List<Person> person;
public PersonDetails(int version) {
super();
this.version = version;
}
...getters / setters / toSting
Person
public class Person implements Serializable{
private static final long serialVersionUID = 3290753964441709903L;
private String identifier;
private String role;
public Person(String identifier, String role) {
super();
this.identifier = identifier;
this.role = role;
}
...getters / setters / toSting
The problem isn't the JSON necessarily, it's that the JSON structure doesn't match your PersonResponse class. There's an info variable in PersonResponse that requires an array of what I assume to be persons, in the second example you're trying to push an object in there, which you can't. You have to either change your JSON, which you don't seem to want in this case, or the class you're trying to parse it to.
You need to restructure the info variable in PersonResponse to match the object you're trying to parse to it.
I have a Pojo class that looks like this
#Table(name = "attachments")
public class Attachment {
#Column(name="id")
private Integer id;
#Column(name = "uuid")
private String uuid;
#Column(name = "customer_id")
private Integer customerId;
#Column(name = "size")
private Integer size;
#Column(name = "uploaded_to_minio")
private Boolean uploaded;
#Column(name = "created_at")
private LocalDateTime created;
#Column(name = "updated_at")
private LocalDateTime updated;
#Column(name = "name")
private String name;
#JsonAppend(
attrs = {
#JsonAppend.Attr(value = "DownloadLink")
}
)
public static class DownloadLink {}
// Getters, Setters omitted
}
When I call a REST endpoint, example: localhost:8080/attachments/2500 I get a Json like this. (2500 is customer id in this context and the endpoint returns a List<>)
{
"id": 7,
"uuid": "8980560a-f9af-4ce5-80a6-9da384f8f886",
"customerId": 2500,
"size": 336430,
"uploaded": false,
"created": {
"hour": 11,
"minute": 24,
"second": 8,
"nano": 0,
"dayOfYear": 311,
"dayOfWeek": "WEDNESDAY",
"month": "NOVEMBER",
"dayOfMonth": 7,
"year": 2018,
"monthValue": 11,
"chronology": {
"calendarType": "iso8601",
"id": "ISO"
}
},
"updated": null,
"name": "image_2500_20130916_11_41_17.jpeg"
}
// other json omitted
It successfully returns a List<> of Attachment. Now, What I want to do is add a field "downloadLink" as JsonAppend when individual id is called, like this localhost:8080/attachments/2500/7
{
"id": 7,
"uuid": "8980560a-f9af-4ce5-80a6-9da384f8f886",
"customerId": 2500,
"size": 336430,
"uploaded": false,
"created": [
2018,
11,
7,
11,
24,
8
],
"updated": null,
"name": "image_2500_20130916_11_41_17.jpeg",
"DownloadLink": "http://127.0.0.1:9000/test/8980560a-f9af-4ce5-80a6-9da384f8f886/image_2500_20130916_11_41_17.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=EZXTCHKE2YHUNRPI8JCL%2F20181108%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181108T101712Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=f45023cda340afcb9ba49343c0cf1a5bbb1577e8fcdb52ef59f7c2f25f967874"
}
I can acheive this by a method that looks like this:
#GET
#Path("customers/{customerId}/{id}")
#Produces(MediaType.APPLICATION_JSON)
public String getAttachmentById(#PathParam("customerId") Integer customerId, #PathParam("id") Integer id) throws Exception {
Attachment attachment = attachmentService.getAttachment(customerId, id);
String downloadlink = minioFileServer.getDownloadLinkForFile("test", attachment.getUuid(), attachment.getName());
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.addMixIn(Attachment.class, Attachment.DownloadLink.class);
ObjectWriter writer = mapper.writerFor(Attachment.class).withAttribute("DownloadLink", downloadlink);
String jsonString = writer.writeValueAsString(attachment);
return jsonString;
}
What I really want to do is to return an Attachment instance itself and not String. When I tried doing so like this:
attachment = mapper.readValue(jsonString, Attachment.class);
return attachment;
It gives me plain Json without downloadlink as in first json. When I'm returning String, it gives me what I need, but when I return Pojo it's not showing downloadlink. Can anyone please help me pointing to the right direction? I'd really appreciate any help.
I have json response in the form
{
"total": 782,
"category": {
"id": 1120,
"name": "Computers & Programming"
},
"experts": [
{
"id": 385816,
"name": "Master Z",
"title": "Mr",
"description": " Expert in C++",
"profile": "http://...com/experts.svc/1.0/385816/..../json?.."
}
}
]
}
I am able to parse everything else in the experts[] array except "profile" whose value "http://...com/experts.svc/1.0/385816/..../json?.." has the following json response
{
"id": 385816,
"name": "Master Z",
"title": "",
"reviewsCount": 15359,
"averageRating": 4.87,
"status": 4,
"fee": 19.99,
"languages": "English",
"category": {
"id": 1120,
"name": "Computers & Programming"
}
}
The models used are as follows
1. main pojo:
public class ExpertObj
{
private String total;
private Category category;
private Experts[] experts;
}
Category pojo:
public class Category
{
private String id;
private String name;
}
3.Experts array pojo:
public class Experts
{
private String id;
private String name;
private String title;
private String description;
private String profile;
}
Am using Retrift 2.
private RestManager mManager;
List<Expert> mExperts = new ArrayList<>();
....
Call<ExpertsObj> call = mManager.getExpertsService().listAllExperts();
call.enqueue(new Callback<ExpertsObj>() {
#Override
public void onResponse(Call<ExpertsObj> call, Response<ExpertsObj> response) {
if (response.isSuccess()) {
ExpertsObj expertsObj = response.body();
mExperts = expertsObj.getExperts();
....}
At a loss how to parse the profile-url name-value pair and display the results in a detail layout in android.
Any help is appreciated.
New in android. Please excuse if anything is not explained properly.