I am writing a credit card management system. This is the DB for the project (each user can have many cards and each card can have many transactions)
DB code sample:
{
"_id": 1,
"name": "Jawad",
"lastname": "Zakhour",
"username": "jwdzkh",
"password": "pass123",
"cards": [{
"cardnumber": "1234-5678-9123-4567",
"cardholdername": "yolla kazan",
"expirationdate": "05/09/2021",
"cvv": "256",
"type": "Credit",
"creditlimit": "3500",
"transactions": [{
"date": "03/06/2020",
"amount": 750,
"receiptNo": "EAC-15-123-45678"
}, {
"date": "06/08/2020",
"amount": 320,
"receiptNo": "THY-18-568-5866"
}]
}, {
"cardnumber": "4589-3256-7841-9655",
"cardholdername": "nabil dagher",
"expirationdate": "06/07/2022",
"cvv": "365",
"type": "Debit",
"balance": "5200",
"transactions": [{
"date": "09/11/2019",
"amount": 90,
"receiptNo": "TYH-35-163-5896"
}, {
"date": "10/10/2020",
"amount": 120,
"receiptNo": "NJU-85-586-4287"
}]
}, {
"cardnumber": "8976-3154-3187-3659",
"cardholdername": "jawad zakhour",
"expirationdate": "06/07/2022",
"cvv": "365",
"type": "Debit",
"balance": "12000",
"transactions": [{
"date": "01/02/2018",
"amount": 14,
"receiptNo": "DFG-58-348-9863"
}, {
"date": "04/12/2019",
"amount": 550,
"receiptNo": "FGH-46-008-3478"
}]
}]
}
on java spring boot I have three models User, Card, Transaction.
#Data
#AllArgsConstructor
#Document(collection = "Vault")
public class Card {
private String cardnumber;
private String cardholdername;
private String expirationdate;
private String cvv;
private String type;
private String creditlimit;
private String balance;
private List<Transaction> transactions;
}
#Data
#AllArgsConstructor
#Document(collection = "Vault")
public class User {
#Id
private int id;
private String name;
private String lastname;
private String username;
private String password;
private List<Card> cards;
}
#Data
#AllArgsConstructor
public class Transaction {
private String date;
private int amount;
private String receiptNo;
}
and also I have created 2 repositories CardsRepository and UserRepository
How can I Return all cards of a specific user?
now i want to get all the transactions for a specific card how is that possible?
since every card has a list it should be possible..
To get transactions of a specific card, just use filter(<some_predicate>). For example,
val user = userRepository.findById(id).orElseThrow(() -> UserNotFoundException(id));
val transactions = user.getCards()
.stream()
.filter(card -> card.getCardNumber().equals("1111.."))
.findFirst()
.map(Card::getTransactions)
.orElseGet(Collections::emptyList);
Here, if a specific card that matches the predicate that cardNumber equals some input, it will be passed on to the next operation in the stream pipeline and findFirst() returns the first occurrence of such card.
Edit with request:
#GetMapping("/users/{username}/cards/{cardnumber}/transactions")
public List<Transaction> getTransactionByCardnumber(#PathVariable String username, #PathVariable String cardnumber) {
val user = userRepository.findById(id).orElseThrow(() -> UserNotFoundException(id));
val transactions = user.getCards()
.stream()
.filter(card -> card.getCardnumber().equals(cardnumber))
.findFirst()
.map(Card::getTransactions)
.orElseGet(Collections::emptyList);
}
Related
I'm working in a REST API (Java, SpringBoot, QueryDsl...) and I would like to customize the fields that I have in the result. By default I'm obtaining all fields of the entity but the fields that I need depends on the request. This should be something dynamic.
As far as I know, using projections I can obtain something like this but I have to declare previously the projection and I would like to work with something dynamic not static. On the other hand I have seen than something like GraphQL allows this behaviour but I would have to refactor everything.
Has anyone had this problem before?
This is my code:
BaseRestCRUDController
public abstract class BaseRestCRUDController<T extends EntityBase, V extends BaseDTO> {
#GetMapping("/list")
public ResponseEntity<List<V>> findAll(Predicate predicate, Pageable pageable) {
log.info("FindAll");
return new ResponseEntity(getCRUDService().findAll(predicate, pageable), HttpStatus.OK);
}
}
ExampleController
#RestController
#RequestMapping("/api/example")
public class ExampleController
extends BaseRestCRUDController<Example, ExampleDTO> {
#Autowired
private ExampleService ExampleService;
#Override
public ResponseEntity<List<ExampleDTO>> findAll(
#QuerydslPredicate(root = Example.class) Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
#Override
protected CRUDService<Example, ExampleDTO> getCRUDService() {
return ExampleService;
}
}
Example (Entity)
public class Example {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "creation_date")
private Instant creationDate;
#Column(name = "last_update")
private Instant lastUpdate;
#Column(name = "erasure_date")
private Instant erasureDate;
}
http://localhost:8080/api/example/list?name=test&page=0&size=5&sort=id,desc
http://localhost:8080/api/example/list?name=foo&page=1&size=10&sort=id,desc
http://localhost:8080/api/example/list?page=0&size=2&sort=id,asc
[
{
"id": 1,
"name": "e1",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 2,
"name": "e2",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
},
{
"id": 3,
"name": "e3",
"creationDate": "2021-11-15T23:00:00Z",
"lastUpdate": null,
"erasureDate": null
}
]
How can I obtain something like this without use projections?
http://localhost:8080/api/example/list?fields=id,name&page=1&size=10&sort=id,desc
[
{
"id": 1,
"name": "e1"
},
{
"id": 2,
"name": "e2"
},
{
"id": 3,
"name": "e3"
}
]
http://localhost:8080/api/example/list?fields=name&page=1&size=10&sort=id,desc
[
{
"name": "e1",
},
{
"name": "e2",
},
{
"name": "e3",
}
]
#Ignore
private Instant creationDate;
Try this.
You can use #Ignore on getter,setter or fields.
Currently I'm trying to write a site that interacts with a public API using Feign and Spring.
I'm having trouble deciding how to handle the object mapping for deeply nested JSON.
Ex:
[
{
"type": "console",
"category": "Console",
"result_count": 1,
"shown_count": 1,
"result": [
{
"name": "Nintendo Switch",
"id": "nintendo-switch",
"platform": {
"name": "Nintendo",
"category": "nintendo",
"type": "platform"
},
"image": {
"url": "https://encrypted-tbn1.gstatic.com/shopping?q=tbn:ANd9GcRqJYIheMDjTE9WAHjMSW4bjh7OplS7Bep9CdsBBLWMwGdXim7xOG4&usqp=CAc",
"height": 409,
"width": 631
},
"min_price": 205,
"variations": [
{
"items": [
{
"hex_code": "#696969",
"name": "Gray",
"id": "space-gray",
"type": "color"
},
{
"hex_code": "#C0C0C0",
"name": "Silver",
"id": "silver",
"type": "color"
}
],
"name": "Color",
"type": "color"
},
{
"items": [
{
"name": "Nintendo",
"id": "nintendo",
"type": "platform"
}
],
"name": "Platform",
"type": "platform"
}
]
}
]
}
]
As of now, I have a single Java file with a class for each object in the JSON, and I've considered having the Object mapper just put everything into a HashMap. Is there a more elegant way to do this?
public class SearchResults {
private List<SearchResult> products;
private int resultCount;
private String type;
}
class SearchResult {
private String name;
private String slug;
private Image image;
}
class Image {
private String URL;
private String height;
private String width;
}
Based on the json file provided i have designed the classes and also provided the code to parse the json file to java
public class Console{
String type;
String category;
int result_count;
int show_count;
Result [] result;
}
public class Result{
String name;
String id;
Platform platform;
Image image;
int mini_price;
Variation [] variations;
}
public class Platform{
String name;
String category;
String type;
}
public class Image{
String url;
int height;
int width;
}
public class Variation{
String name;
String type;
Item [] items;
}
public class Item{
String hex_code;
String name;
String id;
String type;
}
code to parse:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
Console[] consoles = objectMapper.readValue(ResourceUtils.getFile("path of json file"), Console[].class);
logger.info("Continents -> {}",(Object)continents);
for(Console console:consoles) {
//read the data accordingly
}
I want to remove items from a list with a conditional value that comes from another List. Here are the objects
public class Student{
private String name;
private String age;
private List<Course> listCourses;
//Setters and getters
}
public Class Course{
private String courseName;
private List<CourseDetail> listCoursesDetail;
//Setters and getters
}
public Class CourseDetail{
private String id;
private String grade;
private String professor;
//Setters and getters
}
So as you can see the object Student has a list, inside that list there is another list from the object CourseDetail. What I want to achieve is to filter or remove elements from private List<CourseDetail> listCoursesDetail where ID is not equal to id from this other object.
public class Process{
private String name;
private List<ProcessDetail> listProcessDetail;
//Setters and getters
}
public class ProcessDetail{
private String id;
//Setters and getters
}
Assume the object Process is populated as follows
{
"name": "process1",
"listProcessDetail": [
{
"id": "456"
},
{
"id": "666"
},
{
"id": "555"
}
]
}
Student is populated as follows.
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "Math",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "144",
"grade": "90",
"professor": "Marcus"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The expected result will be:
{
"name": "Kevin",
"age": "22",
"listCourses": [
{
"courseName": "456",
"listCoursesDetail": [
{
"id": "666",
"grade": "88",
"professor": "Xavier"
},
{
"id": "555",
"grade": "100",
"professor": "Joe"
}
]
}
]
}
The element from listCoursesDetail with ID 144 was removed since there is no such value in the object Process.
So far I have these:
Set<String> ids = Process.getListProcessDetail().stream().map(ProcessDetail::id).collect(Collectors.toSet());
In these I stored all the ID's on a Set.
Then my attempt to remove the items:
List<Course> courses = Student.getListCourses().stream().filter(c -> c.getListCoursesDetail().stream().anyMatch(d -> ids.contains(d.getId()))).collect(Collectors.toList());
With these lines of code I get the same Student object as nothing happened.
Assuming you want to modify the existing objects, not create a clone of them with shorter lists, you'd do it like this:
student.getListCourses().forEach(c ->
c.getListCoursesDetail().removeIf(d -> ! ids.contains(d.getId())));
I have the following JSON structure:
{
"status": "Completed",
"notes": null,
"members": {
"0": {
"year": "2",
"details": {
"id": "14899975",
"anotherId": "11013306"
},
"aName": "Fred",
"amounts": {
"First": 589.48,
"Second": 1000,
"Third": 339.48
}
},
"1": {
"year": "2",
"details": {
"id": "14899976",
"anotherId": "11013306"
},
"aName": "George",
"amounts": {
"First": 222.22,
"Second": 2000,
"Third": 22.22
}
},
"2": {
"year": 1,
"details": {
"id": "14899976",
"anotherId": "11013306"
},
"aName": "Albert",
"amounts": {
"First": 333.33,
"Second": 3000,
"Third": 33.33
},
}
}
}
I am using Spring RESTTemplate and JacksonMapping2HttpMessageConverter, and the following structures to receive the result of parsing the above JSON structure:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response {
private String status;
private String notes;
private Map<String,Struct1> quotes;
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Struct1 {
private int year;
private Struct2 details;
private String aName;
private Struct3 amounts;
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Struct2 {
private String id;
private String anotherId;
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Struct3 {
private float First;
private float Second;
private float Third;
}
All of these also have appropriate setters and getters for all fields.
My problem is that the number values in Struct3 are not filled in. I've tried making them float, Float, String, and BigDecimal, and the result is either null or 0.0.
I've tried putting a breakpoint in the setter for the first field, hoping
What am I missing? Can the capital letters in the JSON be causing a problem, do I need alternate field names?
It turned out to be the capital letters at the beginning of the field names; I added annotations like #JsonProperty("First") on the line before the getter of the field, and renamed the field to first, and now it's working.
I'm trying to use Gson to parse this JSON:
{
"status": "status",
"lang": "lang",
"guid": "guid",
"name": "name",
"tags": "tags",
"address": "address",
"description": "description",
"size": "M",
"url": "http:\/\/",
"email": "mymail#mysite.com",
"fax": "",
"tel": "000 000 00 00",
"total_votes": "0",
"total_value": "0",
"rate": 5,
"open2424": "0",
"category_main_name": "category_main_name",
"category_name": "category_name",
"category_main_name2": "category_main_name2",
"category_name2": "category_name2",
"category_main_name3": "category_main_name3",
"category_name3": "category_name3",
"park_type": "park_type",
"park_handicap": "0",
"park_free": "1",
"park_description": "",
"datemodinfo": "2012-12-15 18:18:05",
"sponsor": "2",
"sponsorstart": "2012-12-16 13:38:51",
"sponsorend": "2013-12-16 13:38:51",
"zip": "zip",
"town": "town",
"area": "area",
"latitude": "latitude",
"longitude": "longitude",
"distance_info": {
"distance": 10,
"unit": "unit"
},
"image": "image",
"url": "url",
"open": "1",
"openinghours": [{
"schedules": {
"day0": {
"periods": [{
"from": "09:00",
"to": "12:30"
},
{
"from": "14:00",
"to": "18:00"
}],
"date": "2013-08-12"
},
"day1": {
"periods": [{
"from": "09:00",
"to": "12:30"
},
{
"from": "14:00",
"to": "18:00"
}],
"date": "2013-08-13"
},
"day2": {
"periods": [{
"from": "09:00",
"to": "12:30"
},
{
"from": "14:00",
"to": "18:00"
}],
"date": "2013-08-14"
},
"day3": {
"periods": [{
"from": "09:00",
"to": "12:30"
},
{
"from": "14:00",
"to": "18:00"
}],
"date": "2013-08-15"
},
"day4": {
"periods": [{
"from": "09:00",
"to": "12:30"
},
{
"from": "14:00",
"to": "18:00"
}],
"date": "2013-08-16"
},
"day5": {
"date": "2013-08-17"
},
"day6": {
"date": "2013-08-18"
}
},
"title": "title"
}]
}
I parse the JSON with this code:
Gson gson = new Gson();
new GsonBuilder().serializeNulls().create();
Reader reader = new InputStreamReader(source);
response = gson.fromJson(reader, ResponseShow.class);
This is my ResponseShow class:
public class ResponseShow {
#SerializedName("status")
public String status;
#SerializedName("lang")
public String lang;
#SerializedName("guid")
public String guid;
#SerializedName("name")
public String name;
#SerializedName("tags")
public String tags;
#SerializedName("address")
public String address;
#SerializedName("description")
public String description;
#SerializedName("size")
public String size;
#SerializedName("url")
public String url;
#SerializedName("email")
public String email;
#SerializedName("fax")
public String fax;
#SerializedName("tel")
public String tel;
#SerializedName("total_votes")
public String total_votes;
#SerializedName("total_values")
public String total_values;
#SerializedName("rate")
public String rate;
#SerializedName("open2424")
public String open2424;
#SerializedName("category_main_name")
public String category_main_name;
#SerializedName("category_name")
public String category_name;
#SerializedName("category_main_name2")
public String category_main_name2;
#SerializedName("category_name2")
public String category_name2;
#SerializedName("category_main_name3")
public String category_main_name3;
#SerializedName("category_name3")
public String category_name3;
#SerializedName("park_type")
public String park_type;
#SerializedName("park_handicap")
public String park_handicap;
#SerializedName("park_free")
public String park_free;
#SerializedName("park_description")
public String park_description;
#SerializedName("datemodinfo")
public String datemodinfo;
#SerializedName("sponsor")
public String sponsor;
#SerializedName("sponsorstart")
public String sponsorstart;
#SerializedName("sponsorend")
public String sponsorend;
#SerializedName("town")
public String town;
#SerializedName("area")
public String area;
#SerializedName("latitude")
public String latitude;
#SerializedName("longitude")
public String longitude;
#SerializedName("distance_info")
public Map<String, String> distance_info = new HashMap<String, String>();
#SerializedName("zip")
public String zip;
#SerializedName("image")
public String image;
#SerializedName("ligoo_url")
public String ligoo_url;
#SerializedName("open")
public int open;
public List<openinghours> openinghours;
#SerializedName("query")
public String query;
}
This is my openinghours class:
public class openinghours {
public List<schedules> schedules;
#SerializedName("title")
public String title;
}
This is my Schedules class:
public class schedules {
public List<day0> day0;
public List<day1> day1;
public List<day2> day2;
public List<day3> day3;
public List<day4> day4;
public List<day5> day5;
public List<day6> day6;
}
And my day0 class:
public class day0 {
#SerializedName("date")
public String date;
public List<periods> periods;
}
The problem is that I get the following error while trying to parse day0:
Error: java.lang.IllegalStateException: Expected BEGIN_ARRAY goal was BEGIN_OBJECT at line 1 column 2414
Your problem is in the class openinghours (which btw should be in uppercase!). There, you're trying to parse the field "schedules" into a List, and as you can see in your JSON, it's not a List, but an object (it's surrounded by { }).
Concretely, the "schedules" field looks like:
"schedules": {
"day0": {
...
},
"day1": {
...
},
...
}
So, the fastest solution for you would be just to replace the type of the attribute schedules in your openinghours class by:
public schedules schedules;
Because the JSON field "schedules" is an object that contains several fields day0, day1, etc... And that's exactly what your class schedules is... So, this should work for you!
Anyway, the best solution is to use a Map this in your openinghours class:
public Map<String, Day> schedules;
This is the best option because this is exactly what the JSON field "schedules" represents... Moreover, this way you can have only one class Day instead of many classes day0, day1, etc... which makes much much more sense!
Try to replace
public class openinghours {
public List<schedules> schedules;
#SerializedName("title")
public String title;
}
//by
public class openinghours {
public schedules schedules;
#SerializedName("title")
public String title;
}
You might define a class for your distance_info():
class DistanceInfo{
private int distance;
private unit;
}
//change
#SerializedName("distance_info")
public Map<String, String> distance_info = new HashMap<String, String>();
//to
#SerializedName("distance_info")
public DistanceInfo distance_info;