Related
Quick explanation
While fetching a nested collection from a JPQL query it serializes one parent object for each nested object. The intended behavior would be serializing one single parent object with a single collection/set of objects.
Using Spring DATA/DATA REST
JPQ Query
#Query(
"""
select distinct ticketOrder.id as id, user.name as name, ticket as tickets from TicketOrder ticketOrder
inner join User user on user.id = ticketOrder.user.id
inner join Ticket ticket on ticket.ticketOrder.id = ticketOrder.id
where user.uid = :uid
"""
)
fun findByUserUid(uid: UUID, pageable: Pageable): Page<TicketOrderProjection>
Related Entity Classes
TicketOrder
class TicketOrder(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id", nullable = false)
val id: Long? = null,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false)
val user: User,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, updatable = false)
val raffle: Raffle? = null,
#OneToMany(mappedBy = "ticketOrder", cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
var tickets: MutableList<Ticket> = mutableListOf(),
#OneToOne(fetch = FetchType.LAZY)
var transaction: Transaction?,
#CreationTimestamp
#Column(name = "created_at", updatable = false)
val createdAt: Timestamp? = null,
#UpdateTimestamp
#Column(name = "updated_at")
val updatedAt: Timestamp? = null,
#Column(name = "completed_at", nullable = true)
var completedAt: LocalDateTime? = null,
#Column
var checkoutCode: String? = null,
#Enumerated(EnumType.STRING)
#Column(name = "order_status", nullable = false)
var status: TicketOrderStatus
)
/* ... omitted for brevity */
Ticket
#Entity
class Ticket(
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ticket_id", nullable = false)
val id: Long? = null,
#Column(name = "ticket_number", nullable = false, columnDefinition = "UNIQUE")
val ticketNumber: Int,
#ManyToOne
#JoinColumn
var ticketOrder: TicketOrder? = null,
#ManyToOne
#JoinColumn
var raffle: Raffle
)
User
class User(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", nullable = false)
val id: Long? = null,
#Column(nullable = false)
val name: String,
#Column(nullable = false)
val phone: Long,
#Column(nullable = false)
val email: String,
#Column(nullable = false, updatable = false)
val uid: UUID = UUID.randomUUID(),
#CreationTimestamp
#Column(name = "created_at", updatable = false)
val createdAt: Timestamp? = null
)
Projections
TicketOrderProjection
#Projection(types = [TicketOrder::class])
interface TicketOrderProjection {
val id: Long
val name: String
val tickets: Set<TicketsProjection>
}
TicketsProjection
#Projection(types = [Ticket::class])
interface TicketsProjection {
#get:Value("#{target.id}")
val id: Int
}
Finally
Response at RestController
{
"content": [
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 21
}
]
},
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 22
}
]
},
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 23
}
]
}
],
"pageable": {
// omitted
}
Expected response
{
"content": [
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 21,
},
{
"id": 22
},
{
"id": 23
}
]
},
],
"pageable": {
// omitted
}
I have a data set as follows:
[
{
"Id": 1,
"Country": "Uruguay",
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 2,
"Country": "Uruguay",
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 3,
"Country": "Germany",
"Name": "Foo",
"Status": "Completed",
},
]
I want to transform and sort it by Country so that it looks as follows:
[
{
"Country": "Uruguay",
"Details": [
{
"Id": 1,
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 2,
"Name": "Foo",
"Status": "Completed",
},
],
},
{
"Country": "Germany",
"Details": [
{
"Id": 3,
"Name": "Foo",
"Status": "Completed",
},
],
},
],
These are the classes in C#:
public class Countries {
public int Id { get; set; }
public string Country { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
public class Details {
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
public class CountryList {
public string Country { get; set; }
public List<Details> Details { get; set; }
}
Some of what I have tried looks as followed:
var foo = countries
.GroupBy(x => new Details { Id = x.Id, Name = x.Name, Status = x.Status })
.Select( y => new CountryList
{
// Country = y.Key.
}
var foo = countries
.GroupBy(x => x.Country)
.Select( y => new CountryList
{
// Country = y.Key.
Details = y.GroupBy(a => new Details
{
Id = a.Id,
Name = a.Name,
Status = a.Status
}).ToList()
}
I am having trouble working out how to use LINQ to solve this. I have done a handful of GroupBy operations in the past, but I wasn't able to work this one out. How do I transform my dataset into the desired result?
You do not need second GroupBy
var foo = countries
.GroupBy(x => x.Country)
.Select(y => new CountryList
{
Country = y.Key,
Details = y.Select(a => new Details
{
Id = a.Id,
Name = a.Name,
Status = a.Status
}).ToList()
};
You can take advantage of the .GroupBy() overload that lets you define a resultSelector to create your CountryLists and populate their Details:
var countries = new List<Countries>
{
new() { Id = 1, Country = "Uruguay", Name = "Foo", Status = "Completed" },
new() { Id = 2, Country = "Uruguay", Name = "Foo", Status = "Completed" },
new() { Id = 3, Country = "Germany", Name = "Foo", Status = "Completed" },
};
List<CountryList> countryList = countries
.GroupBy(
c => c.Country,
( country, matches ) => new CountryList()
{
Country = country,
Details = matches.Select(match => new Details
{
Id = match.Id,
Name = match.Name,
Status = match.Status
}).ToList()
})
.ToList();
, ( country, matches ) => new CountryList() { ... } being the resultSelector.
Example fiddle here.
try this
var orig = JsonConvert.DeserializeObject<List<Countries>>(json);
List<CountryList> countries = orig.GroupBy(o => o.Country)
.Select(x => new CountryList {
Country = x.Key,
Details = x.Select(o => new Details {Id=o.Id,Name=o.Name,Status=o.Status} ).ToList()
}).ToList();
I have two tables connected with a many to many relationship using a composite key:
Table1
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "user_name")
private String userName;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String password;
private String authorization;
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JsonManagedReference
private List<UserProduct> userProducts = new ArrayList<>();
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Orders> orders = new ArrayList<>();
Table2
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private int price;
private double mass;
private double alcohol;
private String picture;
private int amount;
#OneToMany(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserProduct> userProducts = new ArrayList<>();
#OneToMany(
mappedBy = "orders",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<OrderProduct> orderProducts = new ArrayList<>();
Table with composite key
#Entity
#Table(name = "user_product")
public class UserProduct {
#EmbeddedId
private UserProductId id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
#JsonBackReference
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("productId")
private Product product;
#Column(name = "amount_of_new_products")
private int amountOfNewProducts;
When I make a REST call to UserProduct table so i can update the product value using this payload:
{
"user": 4,
"product": 2,
"amountOfNewProducts": 32
}
it writes the information depending on what is the user id and not the product id. For this payload it would write in like this:
{
"id": 3,
"name": "Grimbergen Blanche",
"price": 132,
"mass": 0.33,
"alcohol": 6.0,
"picture": "https://i.imgur.com/qIq1OrC.png",
"amount": 502,
"userProducts": [],
"orderProducts": []
},
{
"id": 4,
"name": "Grimbergen Blonde",
"price": 132,
"mass": 0.33,
"alcohol": 6.7,
"picture": "https://i.imgur.com/OnioHd5.png",
"amount": 435,
"userProducts": [
{
"id": {
"productId": 2,
"userId": 4
},
"product": {
"id": 2,
"name": "Lav Premium",
"price": 73,
"mass": 0.33,
"alcohol": 4.9,
"picture": "https://i.imgur.com/T3gCAOE.png",
"amount": 1862,
"userProducts": [],
"orderProducts": []
},
"amountOfNewProducts": 32
}
],
"orderProducts": []
},
So basically even though i passed in 2 as product id the information will be written in product with id 4 just because the users id is 4. Any kind of a clue where I might me messing up would be appreciated.
You need to fix mappedBy = "user" in Product entity. It must be "product", like this:
#OneToMany(
mappedBy = "product",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<UserProduct> userProducts = new ArrayList<>();
Also check that you have correct #JoinColumn in UserProductId (ufortunately, you didn't put code for it to the question).
I have a native query in Spring-boot from which I want to get result using rest call, but I'm doing something wrong which is not giving me result. Please see my code below
When I call the repository from controller, it is giving me result, but when I try to call through service implementation, it is throwing me error.
Mysql result for select query I used:
mysql> select * from cricket_match;
[{ "id": 1,
"unique_id": 0,
"date": "2019-09-29T00:00:00.000Z",
"match_started": "Yes",
"team2": "St Lucia Zouks",
"team1": "Barbados Tridents"
},
{
"id": 2,
"date": "2019-08-08",
"team1": "India",
"unique_id": 12345,
"team2": "Australia",
"match_started": "No"
}]
mysql> SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC;
[ {"unique_id": 0,
"date": "2019-09-29T00:00:00.000Z",
"match_started": "Yes",
"team2": "St Lucia Zouks",
"weight": 1,
"team1": "Barbados Tridents"
},
{
"date": "2019-08-08",
"weight": 1,
"team1": "India",
"unique_id": 12345,
"team2": "Australia",
"match_started": "No"
}]
MatchCount.java
package com.stackroute.matchrecommendationservice.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "match_recomendation")
public class MatchCount {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "unique_id")
private int unique_id;
#Column(name = "team1")
private String teamOne;
#Column(name = "team2")
private String teamTwo;
#Column(name = "date")
private String matchDate;
#Column(name = "match_started", columnDefinition = "TINYINT(1)")
private boolean matchStarted;
#Column(name = "user_id")
private String userId;
}
MatchCountRepository.java
import com.stackroute.matchrecommendationservice.domain.MatchCount;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.List;
import java.util.Map;
#RepositoryRestResource
public interface MatchCountRepository extends JpaRepository<MatchCount, Integer> {
List<MatchCount> findByUserId(String userId);
#Query(value = "SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC", nativeQuery = true)
public List<Map<String,Object>> findRecommendations();
#Query(value = "SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC", nativeQuery = true)
public List<MatchCount> findByRecommendations();
}
MatchRecommendationService.java
package com.stackroute.matchrecommendationservice.service;
import com.stackroute.matchrecommendationservice.domain.MatchCount;
import com.stackroute.matchrecommendationservice.exception.MatchAlreadyExistsException;
import com.stackroute.matchrecommendationservice.exception.MatchNotFoundException;
import java.util.List;
public interface MatchRecommendationService {
List<MatchCount> findByRecommendationServiceCall();
}
MatchRecommendationImpl.java
package com.stackroute.matchrecommendationservice.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.stackroute.matchrecommendationservice.domain.MatchCount;
import com.stackroute.matchrecommendationservice.exception.MatchAlreadyExistsException;
import com.stackroute.matchrecommendationservice.exception.MatchNotFoundException;
import com.stackroute.matchrecommendationservice.repository.MatchCountRepository;
import com.stackroute.rabbitmq.domain.MatchCountDTO;
#Service
public class MatchRecommendationImpl implements MatchRecommendationService{
private MatchCountRepository matchCountRepository;
#Autowired
public MatchRecommendationImpl(MatchCountRepository matchCountRepository) {
this.matchCountRepository = matchCountRepository;
}
#Override
public List<MatchCount> findByRecommendationServiceCall() {
var ResultMatches = (List<MatchCount>)matchCountRepository.findByRecommendations()
return ResultMatches;
}
}
MatchRecommendationController.java
package com.stackroute.matchrecommendationservice.controller;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.stackroute.matchrecommendationservice.repository.MatchCountRepository;
import com.stackroute.matchrecommendationservice.service.MatchRecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.stackroute.matchrecommendationservice.domain.MatchCount;
import com.stackroute.matchrecommendationservice.exception.MatchAlreadyExistsException;
import com.stackroute.matchrecommendationservice.exception.MatchNotFoundException;
import com.stackroute.matchrecommendationservice.service.MatchRecommendationService;
#CrossOrigin
#RestController
#RequestMapping(path = "/api/v1/matchservice")
public class MatchRecommendationController {
private ResponseEntity responseEntity;
private MatchRecommendationService matchRecommendationService;
#Autowired
public MatchRecommendationController(final MatchRecommendationService matchService) {
this.matchRecommendationService = matchRecommendationService;
}
#Autowired
private MatchCountRepository matchCountRepository;
//Getting Result
#GetMapping("/{userId}/matchrecommendations")
public List<Map<String, Object>> getMatchrecommendations(){
return matchCountRepository.findRecommendations().stream().collect(Collectors.toList());
}
//Not Getting
#GetMapping("/matchrecommendationnew")
public String getMyMatchRecommendation(Model model) {
var results = (List<MatchCount>) matchRecommendationService.findByRecommendationsServiceCall();
model.addAttribute("results", results);
return "results";
}
}
From the controller when tested in POSTMAN, for
http://localhost:8080/api/v1/matchservice/matchrecommendations result is as below
[ {"unique_id": 0,
"date": "2019-09-29T00:00:00.000Z",
"match_started": "Yes",
"team2": "St Lucia Zouks",
"weight": 1,
"team1": "Barbados Tridents"
},
{
"date": "2019-08-08",
"weight": 1,
"team1": "India",
"unique_id": 12345,
"team2": "Australia",
"match_started": "No"
}]
For http://localhost:8080/api/v1/matchservice/matchrecommendationnew
I don't result, which should be same as above & below is the error
{
"timestamp": "2019-10-07T10:55:54.855+0000",
"status": 500,
"error": "Internal Server Error",
"message": "could not execute query; SQL [SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query",
"path": "/api/v1/matchservice/matchrecommendationnew"
}
Error Log:
Hibernate: SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC
2019-10-07 18:03:41.391 WARN 11720 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S0022
2019-10-07 18:03:41.395 ERROR 11720 --- [nio-8080-exec-3] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'id' not found.
2019-10-07 18:03:41.516 ERROR 11720 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; SQL [SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query] with root cause
java.sql.SQLException: Column 'id' not found.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.mysql.cj.jdbc.result.ResultSetImpl.findColumn(ResultSetImpl.java:548) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.mysql.cj.jdbc.result.ResultSetImpl.getInt(ResultSetImpl.java:807) ~[mysql-connector-java-8.0.17.jar:8.0.17]
at com.zaxxer.hikari.pool.HikariProxyResultSet.getInt(HikariProxyResultSet.java) ~[HikariCP-3.2.0.jar:na] ...
Can you please help me how to write the correct controller mehtod to retrive the data and the Junit test case, for it.***
Thanks All, I was able to get the result when I changed the return type of
findByRecommendationServiceCall() to List<Map<String,Object>> from List<MatchCount>
The resultant changes are as below
MatchCountRepository.java
#Query(value = "SELECT unique_id, date, CASE WHEN match_started = 1 THEN 'Yes' ELSE 'No' END match_started, team1, team2, count(unique_id) AS weight FROM cricket_match GROUP BY unique_id, date, match_started, team1, team2 ORDER BY COUNT(unique_id)DESC", nativeQuery = true)
public List<Map<String,Object>> findByRecommendations();
MatchRecommendationService.java
List<Map<String,Object>> findByRecommendationServiceCall();
MatchRecommendationImpl.java
```
#Override
public List<Map<String,Object>> findByRecommendationServiceCall() {
var ResultMatches = (List<Map<String,Object>>)matchCountRepository.findByRecommendations()
return ResultMatches;
}
```
MatchRecommendationController.java
//Not Getting
#GetMapping("/matchrecommendationnew")
public String getMyMatchRecommendation(Model model) {
var results = (List<Map<String,Object>> matchRecommendationService.findByRecommendationsServiceCall();
model.addAttribute("results", results);
return results;
}
As the title suggests, I'm trying to add information held in one JsonBuilder object to a second JsonBuilder object.
Currently I have this:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft
]
}
def userHolidayBuilder = new JsonBuilder()
userHolidayBuilder user.holidayEvents.collect { usr ->
[
'Start Date': usr.startDate,
'End Date': usr.endDate,
'Days': usr.days
]
}
def userAndHolidays = userBuilder + userHolidayBuilder
return userAndHolidays.toPrettyString()
}
user.holidayEvents is a list of objects representing holidays and it could be empty or have any number of objects in it. This made me hesitant of doing something like:
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft
'Holiday': usr.holidayEvents[0].startDate
'Holiday': usr.holidayEvents[0].endDate
'Holiday': usr.holidayEvents[0].days
]
}
As I would only get the amount of holidays I write code for. It would also throw an exception if a user had no holidays and I told it look at usr.holidayEvents[1] as it's outside of the list range.
I've also tried nesting a .collect like this
def userBuilder = new JsonBuilder()
userBuilder {
'Name' user.userName,
'Allowance' user.allowance,
'Total Holidays in Calendar' user.totalHolidaysInCal,
'Holidays Booked' user.numHolidaysBooked,
'Holidays Taken' user.numHolidaysTaken,
'Holidays Remaining' user.totalHolidaysLeft,
'Holidays' user.holidayEvents.collect{ evt ->
[
'Start Date': evt.startDate,
'End Date': evt.endDate,
'Days': evt.days
]
}
}
But this returned all the keys except the Holidays key.
Any help would be greatly appreciated!
EDIT - My code now looks like this:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft,
'Holidays': usr.holidayEvents.collect{ evt ->
[
'Start Date': evt.startDate,
'End Date': evt.endDate,
'Days': evt.days
]
}
]
}
}
EDIT 2 - Sample Code
Method to call:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder {
Name:
user.userName
Allowance:
user.allowance
TotalHolidaysInCalendar:
user.totalHolidaysInCal
HolidaysBooked:
user.numHolidaysBooked
HolidaysTaken:
user.numHolidaysTaken
HolidaysRemaining:
user.totalHolidaysLeft
Holidays:
user.holidayEvents.collect { evt ->
[
'Start Date': evt.startDate,
'End Date' : evt.endDate,
'Days' : evt.days
]
}
}
return userBuilder.toPrettyString()
}
User to pass in:
class DyveUserDTO
{
String firstName = "Foo"
String userName = "FooBar"
Integer userID = 42
BigDecimal numHolidaysBooked = 3
BigDecimal numHolidaysTaken = 0
BigDecimal totalHolidaysInCal = 3
BigDecimal totalHolidaysLeft = 12
BigDecimal allowance = 12
List<HolidayObject> holidayEvents = []
}
Holiday objects to go in holidayEvents:
class HolidayObject
{
public Integer userID = 42
public String title = "Foo Holiday"
public String event = "Holiday"
public String amPm = "Full Day"
public String name = "Foo"
public LocalDateTime startDate = LocalDateTime.parse(2015-02-20T00:00:00)
public LocalDateTime endDate = LocalDateTime.parse(2015-02-20T00:00:00)
public BigDecimal days = 1
}
class HolidayObject
{
public Integer userID = 42
public String title = "Foo Holiday Pm"
public String event = "Holiday"
public String amPm = "Pm"
public String name = "Foo"
public LocalDateTime startDate = LocalDateTime.parse(2015-02-23T00:00:00)
public LocalDateTime endDate = LocalDateTime.parse(2015-02-24T00:00:00)
public BigDecimal days = 2
}
each just returns the list it's called upon, collect should be used for events. See the working code below:
import groovy.json.JsonBuilder
class UserEvent {
def start
def end
def days
}
class User {
def name
def events
}
def u1 = new User(name: 'u1', events: [new UserEvent(start: 0, end: 1, days: 1), new UserEvent(start: 0, end: 2, days: 2)])
def u2 = new User(name: 'u2', events: [new UserEvent(start: 0, end: 3, days: 3)])
def users = [u1, u2]
def userBuilder = new JsonBuilder()
userBuilder users.collect { usr ->
[
'name': usr.name,
'events': usr.events.collect { e ->
[
start: e.start,
end: e.end,
days: e.days,
]
}
]
}
print userBuilder.toPrettyString()
EDIT
Below is a working example:
import groovy.json.JsonBuilder
user = new DyveUserDTO()
def userBuilder = new JsonBuilder()
userBuilder {
Name user.userName
Allowance user.allowance
TotalHolidaysInCalendar user.totalHolidaysInCal
HolidaysBooked user.numHolidaysBooked
HolidaysTaken user.numHolidaysTaken
HolidaysRemaining user.totalHolidaysLeft
Holidays user.holidayEvents.collect { evt ->
[
'Start Date': evt.startDate,
'End Date' : evt.endDate,
'Days' : evt.days
]
}
}
println userBuilder.toPrettyString()
class DyveUserDTO {
String firstName = "Foo"
String userName = "FooBar"
Integer userID = 42
BigDecimal numHolidaysBooked = 3
BigDecimal numHolidaysTaken = 0
BigDecimal totalHolidaysInCal = 3
BigDecimal totalHolidaysLeft = 12
BigDecimal allowance = 12
List<HolidayObject> holidayEvents = [new HolidayObject(), new HolidayObject()]
}
class HolidayObject {
public Integer userID = 42
public String title = "Foo Holiday"
public String event = "Holiday"
public String amPm = "Full Day"
public String name = "Foo"
public String startDate = '2015-02-20T00:00:00'
public String endDate = '2015-02-20T00:00:00'
public BigDecimal days = 1
}
No colons : needed. See the sample here. Also I have no Joda dependency so replaced with String.