Solution
I had previously tried adding accessors to the LineItem class like
public String getItemNo() {
return itemNo;
}
and changing the FTL from ${lineItem.itemNo} to ${lineItem.getItemNo()} but that didn't work. Solution was to add the accessors but not change the FTL (keep it as ${lineItem.itemNo}.
Background
I'm using Freemarker to format some emails. In this email I am required to list a number of lines of product information like on an invoice. My goal is to pass a list of Objects (within a Map) so that I may iterate over them in the FTL. Currently I am having an issue where I am unable to access the objects properties from within the template. I'm probably only missing something small, but at the moment I am stumped.
Java Class using Freemarker
This is a more simplified version of my code in order to more quickly get the point across. LineItem is a public class with public properties (matching the names used here), using a simple constructor to set each of the values. I have also tried using private variables with accessors but that didn't work either.
I am also storing this List of LineItem objects within a Map as I also use the Map for other key/value pairs.
Map<String, Object> data = new HashMap<String, Object>();
List<LineItem> lineItems = new ArrayList<LineItem>();
String itemNo = "143";
String quantity = "5";
String option = "Dried";
String unitPrice = "12.95";
String shipping = "0.00";
String tax = "GST";
String totalPrice = "64.75";
lineItems.add(new LineItem(itemNo, quantity, option, unitPrice, shipping, tax, totalPrice));
data.put("lineItems", lineItems);
Writer out = new StringWriter();
template.process(data, out);
FTL
<#list lineItems as lineItem>
<tr>
<td>${lineItem.itemNo}</td>
<td>${lineItem.quantity}</td>
<td>${lineItem.type}</td>
<td>${lineItem.price}</td>
<td>${lineItem.shipping}</td>
<td>${lineItem.gst}</td>
<td>${lineItem.totalPrice}</td>
</tr>
</#list>
Error
FreeMarker template error:
The following has evaluated to null or missing:
==> lineItem.itemNo [in template "template.ftl" at line 88, column 95]
LineItem.java
public class LineItem {
String itemNo;
String quantity;
String type;
String price;
String shipping;
String gst;
String totalPrice;
public LineItem(String itemNo, String quantity, String type, String price,
String shipping, String gst, String totalPrice) {
this.itemNo = itemNo;
this.quantity = quantity;
this.type = type;
this.price = price;
this.shipping = shipping;
this.gst = gst;
this.totalPrice = totalPrice;
}
}
The LineItem class is missing getter methods for all its attributes. Therefor, Freemarker cannot find them. You should add a getter method for each attribute of LineItem.
For me adding the #CompileStatic to the Model did the Trick.
Related
I have a List result in my Spring Boot service method as shown below:
Country:
String name;
List<State> states;
State:
String name;
Long population;
List<Town> towns;
Town:
String name;
I return Country list from my repository and it has all the related State and Town date for each Country. I want to map this data to a DTO as shown below:
public class CountryDTO {
private Long id;
private String name;
private Long population; //population sum of states by country
private List<Town> towns;
//constructor
}
So, how can I map my Country entity to this CountryDTO properly as explained above? I need the following data:
Country name
Sum of population by each country
Towns of each country
Update: Here is my service method where I tried to use Java Stream and then also ModelMapper, but cannot return desired values :(
List<CountryDTO> countries = countryRepository.findAll().stream()
.flatMap(x -> x.getStates().stream())
.map(x -> new CountryDTO(x.getCountry(), <-- need to return sum of population here -->, ObjectMapperUtils.mapAll(x.getTowns(), TownDTO.class)))
.toList();
You can use Stream#mapToInt to get all the state populations and .sum to add them up.
Stream#flatMap can be used to get all the towns of each state into a single stream.
List<CountryDTO> res = countries.stream().map(c -> new CountryDTO(c.getName(),
c.getStates().stream().mapToInt(State::getPopulation).sum(),
c.getStates().stream().flatMap(s -> s.getTowns().stream()).toList())).toList();
Didn't get your question completely, maybe it will be more helpful if you share the exact response returned by the APIs.
But, going by the use case, if you are getting a list in the response, you always have the option of stream the list, iterate each element, and then use conditional / grouping logics to get the data in the desired way.
For example;
Let's say, you have a list of Object type, for which you have a repository layer configured as ObjetRepo. Now, if you are getting a list of objects, you can stream it like this:
#Autowired
private ObjectRepo objectRepo;
public List<Object> method() {
List<Object> objects = new ArrayList<>();
objectRepo.findAll().forEach(objects::add);
// Here, instead of add, any other logic can be used.
// You can also write a method, and use that method here
// objectRepo.findAll().forEach(objects::someMethod);
return objects;
}
Hope this helps.
I want to create this data stricture:
Map<String, Country> table = new HashMap<>();
table.put("AF", new Country("Afghanistan", "AFG", "004"));
// 300 more
class Country {
private String country;
private String alpha3;
private String number;
public Country(String country, String alpha3, String number) {
this.country = country;
this.alpha3 = alpha3;
this.number = number;
}
}
Second way:
Map<String, Country> table = new HashMap<>();
Table<String, String, Integer> table2 = HashBasedTable.create();
table.put("AF", HashBasedTable.create().put("Afghanistan", "AFG", "004"));
// 300 more
I want to search values into the data structure values based on keys that I send:
numeric code from country name
country code from country name
alpha3 code from country name
What is the bast way to implement this search into the second data structure?
HashMap contains a method called forEach(BiConsumer<? super K,? super V> action) which can be used to iterate through each key-value pair in your HashMap.
Therefore, using a lambda you can do something like this:
//Search a HashMap of employee ID/name records by name, for their ID number.
public int getID(HashMap<Integer, String> employees, String name) {
//Iterate through every key-value pair, performing the lambda's operation on it.
employees.forEach((Integer j, String k) -> {
if(k.equals(name)) return Integer.valueOf(j); //If the value matches the name, return the ID
}
return -1;
}
I am struggling with making this work properly. So I have two CSV Files.
And this One
So the main thing is. I have SearchTerms in 2nd CSV. In first CSV I have SearchTerms also, which should work as a "tag".
What I need is to get product ids from first CSV saved to a List < String > based on, what searchterm from 2nd csv is used. So when Akt (pan) is used, one set of List of IDS is exported. If there is more Akt (pan) sets of ids, they are saved to one list as array I guess.
I tried to read it with CSVloaders and searching it with lookup() method
private final Map<List<String>, Comparison> data = Maps.newHashMap();
public Comparison lookup(String searchTerm) {
return data.get(searchTerm);
}
Where Comparison Class is
public class Comparison {
#Parsed(field = "ProductId1")
private String productId1;
#Parsed(field = "ProductId2")
private String productId2;
#Parsed(field = "ProductId3")
private String productId3;
#Parsed(field = "SearchTerm")
private String SearchTerm;
public String getProductId1() {
return productId1;
}
public String getProductId2(){
return productId2;
}
public String getProductId3(){
return productId3;
}
public List<String> getProductIds(){
List<String> ids = new ArrayList<>();
Collections.addAll(ids, productId1, productId2, productId3);
return ids;
}
}
My solution was bad. I was getting NullPointerException constantly whenever I tried to use lookup() method.
Do you have any ideas how to make this work? Thank oyu
Problem is with the data type of key in your HashMap. It should be a string as per your data, not a List<>.
private final Map<String, Comparison> data = Maps.newHashMap();
public Comparison lookup(String searchTerm) {
return data.get(searchTerm);
}
Then the returning object(typed Comparison) would have the all products Ids for the given search Term.
I have an Customer Object like below.
public class Custoemr {
private String Id;
Private String Name;
Private String Address;
Private String Description;
Setter/Getter;
toString;
}
This is Contained in Map<String, Customer> map, which contains the customerId and Object as key and value respectively. For analysis purposes, I need to collect all the customer description data in String to be written in a file.
To do that I need to Extract data from description in String and not List<String>.
I saw several examples on the internet which collects them as a List<String> but I need it in a single String.
Is there a way to extract the information without iterating I mean by using java Streams.
If I understood correctly:
yourMap.values()
.stream()
.map(Customer::getDescription)
.collect(Collectors.joining(","));
Here is the scenario.
I am trying to get a list of records in my resource layer. It has the following code;
PagedResponse<Person> pagedResponse= new PagedResponse<Person>();
There is a call to Business implementation as
pagedResponse = facadeLocator.getPersonFacade().findAllRecords(getUserId(), fromrow, maxrows);
Now in the Business implementation, I use named query as;
Query query = getNamedQuery("Person.findAll");
I have the response as
pagedResponse = executePagedQuery(query, firstResults, maxResults);
and inside executePagedQuery(), I have;
List resultList = query.getResultList();
The response returned pagedResponse is of custom type PagedResponse class with 2 members;
private Long totalRecords;
private List<T> records;
Now in my Person class, I have
#NamedQuery(name = "Person.findAll", query = "Select DISTINCT(p.personName), p.personAge, p.personGender from Person p where p.personAge = :personAge")
Now here is what happens at runtime.
I get the "records" as Vector with members as
[0] = Object[]
[1] = Object[]
Coming back to the resource layer, I want to iterate through the response and set it in a list
List<Person> personList = new ArrayList<Person>();
So what are the options I have.
I have tried doing
for(Object[] person: pagedResponse.getRecords()) {
Person p = new Person();
p.setPersonName((String)person[0]);
// Setting other values
personList.add(p);
}
But it says incompatible types for the line
for(Object[] person: pagedResponse.getRecords()) {
Just to add, I did not face any incompatible type issue when my query did not return select columns and instead returned all the columns like;
query = "SELECT p FROM Person p WHERE
So I have 2 questions;
1. Why was there no type casting issues when I was returning all the columns using the named query (It showed the type as "Person" and not generic type as showing after using the named query with specific columns)
2. Using the query with specific columns, what is the right approach to set the values returned from the query in the resource layer ?
The query with many individual SELECTed values is supposed to return a list of lists. Maybe you want to define a bean with an appropriate constructor:
package com.foo;
public class PersonData {
private String name;
private int age;
private Sex gender;
public PersonData(String name, int age, Sex gender) {
this.name = name;
this.age= age;
this.gender = gender;
}
// getters/setters
}
And run the query as:
SELECT NEW com.foo.PersonData(DISTINCT(p.personName), p.personAge, p.personGender)
FROM Person p WHERE p.personAge = :personAge
Now getResultList() should return a list of PersonData objects. Though I haven't used the nested new PersonData(DISTINCT(...)) syntax...