How to convert a Java object to key-value string - java

I have this Java object and I will like to convert it into a key-value string joined by ampersand.
private String name;
private int age;
private String address;
private String city;
Convert into this key-value string.
name=John&age=30&address=12st NW Street&city=New York
I have tried Jackson but I dont want a JSON string.
I have tried URIEncoder but I don't need it to be encoded.
Tried looping each property using reflection, but I guess theres a better way.
I have considered toString, but I want something more flexible. Because properties name might change.

I would go with the proposition of #Thomas where you can use for example:
ObjectMapper mapper = new ObjectMapper();
Map<String, String> map = mapper.convertValue(person, Map.class);
String response = map.entrySet().stream()
.map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
.collect(Collectors.joining("&"));
Outputs
name=John&age=30&address=12st NW Street&city=New York

You can override the toString() function to get required format -
private String name;
private int age;
private String address;
private String city;
#Override
public String toString() {
return "name=" + name +
"&age=" + age +
"&address=" + address +
"&city=" + city;
}

Related

how to find an item in a SortedSet in Java

I have a Sorted Set in Java with an object with 2 strings, Name and Age. Name is unique.
Now I have the Name and I want to get the age based on the name.
I have my object:
SortedSet<Person> people;
That has 3 people inside: "John / 35", "James / 21" and "Maria /21"
Based on this, I want to check James age.
How can I do it? The only idea I have is just doing a for, but I guess it should be something easier.
I see two solutions there:
If there really are just this two properties, you could simply convert that to a map, where the name is the key and the age is the value, ( Map<String, Integer> ageMap). Then you can quickly get the age by using ageMap.get("James");.
Edit: To convert you can do this:
Map<String, Integer> ageMap = new HashMap<>();
for (Person p : people) {
ageMap.put(p.getName(), p.getAge());
}
int jamesAges = ageMap.get("James");
If you stay with the Set and the Person class, I would recommend using the streams:
Optional findFirst = set.stream().filter(e -> e.getName().equals("James")).findFirst();
if (findFirst.isPresent()) {
int age = findFirst.get().getAge();
}
Internally, this will probably still use some kind of for, but the real implementation might be a bit more optimized.
I would not use a set for this since you cannot easily retrieve values from a set. I would go with a map. You can populate the map anyway you like.
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "[" + name + ", " + age +"]";
}
}
Map<String, Person> people = new HashMap<>(Map.of("john", new Person("John",35),
"james", new Person("James", 21), "maria", new Person("Maria", 21)));
String name = "James";
Person person = people.get(name.toLowerCase());
System.out.println(person != null
? name + "'s age is "+ person.getAge()
: name + " not found");
prints
James's age is 21

Extract a fields data from a Map<Integer, Object> into a String

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(","));

Mapping String with delimiters to list of strings or to new object

I have a file with some data separated by semicolon. I'm trying to build a stream, which will read file line by line, separate every column of data and map it to new object.
data.txt:
John;Smith;42;shopassistant
Clara;Lefleur;24;programmer
Person.class:
public class Person{
String name;
String lastName;
int age;
String job;
}
I started with something like this:
List<Person> people = Files.lines(Paths.get("src/data.txt"))....
Any ideas?
You can use map like so :
List<Person> people = Files.lines(Paths.get("src/data.txt"))
.map(p -> p.split(";"))//split each line with ';'
.map(p -> new Person(p[0], p[1], Integer.valueOf(p[2]), p[3]))//create a new Person
.collect(Collectors.toList());//then collect the result
Make sure that you have a constructor which hold that information :
public Person(String name, String lastName, int age, String job) {
A Scanner would be more useful for this job, and since java-9 this is much nicer to work with:
Pattern p = Pattern.compile("^(\\w+);(\\w+);(\\w+);(\\w+)$", Pattern.MULTILINE);
List<Person> persons = new Scanner(Paths.get("src/data.txt"))
.findAll(p)
.map(mr -> new Person(mr.group(1), mr.group(2), Integer.valueOf(mr.group(3)), mr.group(4)))
.collect(Collectors.toList());

Accessing properties of Objects within <#list>

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.

Printing data from Object/Hashmap by getters/keys

I have a object like this:
public class Person {
String name = "Any";
int age = 9001;
}
public String getName() {return this.name;}
public int getAge() {return this.age;}
Is there a way to print this data with predefined template as stated below?
Person firstOne = new Person();
print_throw_getters("Name: $name%s Age: $age%i",firstOne);
If no, I can convert an Object in the HashMap:
public HashMap getHashMap {
HashMap test = new HashMap<String,Object>();
test.put("name",this.getName());
test.put("age",this.getAge());
return test;
}
Is there a function to print values by keys in predefined template, like this?
print_throw_keys("Name: $name%s Age: $age%i",firstOne.getHashMap());
I do not want to use the indexes like printf("name: $1%s",string) since templates vary considerably, but the incoming object type is unique.
.
UPD: Thanks for the answers, but the idea is that there will be a file with a list of templates, which will be inserted into the data, for example:
String template1 = "Your selected Name is $name%s and selected age $age%i";
String template2 = "User: $name%s; Age: $age%i";
Accordingly, the objects themselves will contain a lot more getters.
You can use
MapFormat
Example:
String text = "Your selected Name is {name} and selected age {age}";
HashMap test = new HashMap();
test.put("name", "Mr. X");
test.put("age", "21");
System.out.println("Example: " + MapFormat.format(text, test));
Output:
Example: Your selected Name is Mr. X and selected age 21
There is no method in the Java API that can do such a thing, but it is easily implemented:
public static String buildMessage(Map<String, Object> data, String template) {
Pattern p = Pattern.compile("%([A-Za-z0-9_]+);");
Matcher m = p.matcher(template);
int offset = 0;
while(m.find()) {
int start = m.start(1) - 1 + offset;
int end = m.end(1) + 1 + offset;
String field = m.group(1);
String value = data.get(field).toString();
offset += -2 - field.length() + value.length();
template = template.substring(0, start) + value + template.substring(end);
}
return template;
}
and then you can use such method as follows:
String template = "-- this is my name: %name;, and my age: %age; --";
Map<String, Object> data = new HashMap<>();
data.put("name", "Robert Smith");
data.put("age", 20);
System.out.println(buildMessage(data, template));
This method is just a skeleton, if you want to use it for something more serious you'll have to improve this method to do the following:
validate that field names have only A-Z, a-z, 0-9, (or modify the regex)
validate cases where the template has fields not specified in the map
some other things that I'm missing right now
Just override the toString() method on your person object.
in person object
#Override
public String toString() {
return "Name:"+this.name+"age:"+this.age;
}
then use like System.out.printLn(person.toString());
Override toString() function to print from object state.
Example:
#Override
public String toString() {
return "Name: "+this.getName()+" Age: "+this.getAge()";
}
and to use it:
Person p = new Person();
...
System.out.println(p);
Couple of examples:
Ref1
Ref2
Sounds like an application of the Template Method design Pattern ...

Categories