OpenCSV flat to hierarchy structure data parsing - java

I need advice on the issue which I am facing while parsing CSV data in java.
I have a CSV file with data in below format
name, gender, address_first_line, address_second_line, city, number
me, Male, anonymous, random, Auckland, 6545
other, Female, random, anon, Auckland, 6688
I want to parse the CSV file using the openCSV library in below Object Model. I am aware of parsing single class but facing issues while parsing data when multiple classes are involved. Please suggest if there is any other library available which can help me to get the desired result.
Class User {
String name;
String gender;
Address address;
long number;
}
Class Address {
String firstLine;
String secondLine;
}

univocity-parsers has a #Nested annotation you can use:
Try this:
public static class User {
#Parsed
String name;
#Parsed
String gender;
#Nested
Address address;
#Parsed
long number;
#Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", address=" + address +
", number=" + number +
'}';
}
}
public static class Address {
#Parsed(field = "address_first_line")
String firstLine;
#Parsed(field = "address_second_line")
String secondLine;
#Override
public String toString() {
return "Address{" +
"firstLine='" + firstLine + '\'' +
", secondLine='" + secondLine + '\'' +
'}';
}
}
public static void main(String ... args){
StringReader input = new StringReader(
"name,gender,address_first_line,address_second_line, city,number\n" +
"John,M,Somewhere,Else,City,1");
for(User user : new CsvRoutines().iterate(User.class, input)){
System.out.println(user.toString());
}
}
Output:
User{name='John', gender='M', address=Address{firstLine='Somewhere', secondLine='Else'}, number=1}
If you don't like annotations you can map things manually:
CsvRoutines routines = new CsvRoutines();
ColumnMapper mapper = routines.getColumnMapper();
mapper.attributeToColumnName("name", "name");
mapper.attributeToColumnName("gender", "gender");
mapper.attributeToColumnName("number", "number");
mapper.attributeToColumnName("address.firstLine", "address_first_line");
mapper.attributeToColumnName("address.secondLine", "address_second_line");
for (User user : routines.iterate(User.class, input)) {
System.out.println(user.toString());
}
Hope it helps.
Disclaimer: I'm the author of this lib. It's open-source and free (Apache 2.0 license)

Related

Saving List<T> as String

I've been trying to save a list of data as Strings, but when I try a method .toString on List, it returns address in memory instead of data.
public class Item {
Integer price = 20;
Integer modelNumber = 100;
String description = "Description";
String title = "Title";
Boolean wasBought = true;
}
public static void main(String[] args) {
List<Item> data = new ArrayList<>();
data.add(new Item());
System.out.println(data.toString());
}
You need to override toString function in your Item class. Add the following snippet into your Item class:
#Override
public String toString() {
return "Item{" +
"price=" + price +
", modelNumber=" + modelNumber +
", description='" + description + '\'' +
", title='" + title + '\'' +
", wasBought=" + wasBought +
'}';
}
Output:
[Item{price=20, modelNumber=100, description='Description', title='Title', wasBought=true}]
You can convert List to json format string by json utils, e.g. jackson or fastjson, in case you may need to convert it to Objects later.
Simply use Lombok (Add Lombok jar into classpath) #ToString annotate for Item class, it will do the needful output https://projectlombok.org/features/ToString

Reading from CSV file and create object

I'm a complete beginner to Java and I have been given an exercise where I have to read data from a CSV file and then create an object for each line of the file as the program reads the data from the file.
Here is part of the CSV file:
1,Jay, Walker,91 Boland Drive,BAGOTVILLE,NSW,2477
2,Mel, Lowe,45 Ocean Drive,MILLERS POINT,NSW,2000
3,Hugh, Manatee,32 Edgecliff Road,REDFERN,NSW,2016
4,Elizabeth, Turner,93 Webb Road,MOUNT HUTTON,NSW,2290
and so on ...
Here is my code that reads data from the CSV file:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Client_19918424 {
public static void main(String[] args) throws FileNotFoundException {
File inFile = new File("clients.txt");
Scanner inputFile = new Scanner(inFile);
String str;
String[] tokens;
while (inputFile.hasNext()) {
str = inputFile.nextLine(); // read a line of text from the file
tokens = str.split(","); // split the line using commas as delimiter
System.out.println("Client ID: " + tokens[0]);
System.out.println("Client First Name: " + tokens[1]);
System.out.println("Client Sur Name: " + tokens[2]);
System.out.println("Street Address: " + tokens[3]);
System.out.println("Suburb: " + tokens[4]);
System.out.println("State: " + tokens[5]);
System.out.println("Postcode:" + tokens[6]);
System.out.println( );
} // end while
}
}
this is my Client class (have constructor):
public class Client {
private int clientID;
private String firstName;
private String surName;
private String street;
private String suburb;
private String state;
private int postcode;
// constructor
public Client (int ID, String fName, String sName, String str, String sb, String sta, int pCode) {
clientID = ID;
firstName = fName;
surName = sName;
street = str;
suburb = sb;
state = sta;
postcode = pCode;
}
However I don't know how to create a Client object for each line of text file as the program reads data from file.
like for the first line make something like this:
Client client1 = new Client(1, "Jay", "Walker", "91 Boland Drive", "BAGOTVILLE", "NSW", 2477);
And then add it to array:
Client[0] = client1;
can someone help me to solve this question, im really appreciate.
You are almost there.
All that's left to do is to map each token that is already printed to the corresponding fields in the Client class. Since token[0] doesn't really tell what value it holds you could do it in three ways:
while (inputFile.hasNext()) {
str = inputFile.nextLine();
tokens = str.split(",");
// Because tokens[0] is of type String but clientID is of type int,
// we need to parse it and get the integer representation.
int clientID = Integer.parseInt(tokens[0]);
// Both of type String, no parsing required.
String firstName = tokens[1];
String surName = tokens[2];
String street = tokens[3];
String suburb = tokens[4];
String state = tokens[5];
int postcode = Integer.parseInt(tokens[6]);
// Then all that's left to do is to create a new object of `Client` type
// and pass all the gathered information.
Client client = new Client(clientID, firstName, surName, street, suburb, state, postcode);
System.out.println(client + "\n");
}
At this moment if we try to print the client (last line) we will get something like this: com.example.demo.Client#30a3107a. That's because we didn't tell how we want our object to be displayed. For that toString() method in Client class has to be overriden like so:
#Override
public String toString() {
return "Client ID: " + clientID + "\n" + "Client First Name: " + firstName + "\n"
+ "Client Sur Name: " + surName + "\n" + "Street Address: " + street + "\n"
+ "Suburb: " + suburb + "\n" + "State: " + state + "\n" + "Postcode: " + postcode;
}
It will give the exact output that is in your example.
It is achievable to create the class by passing those tokens directly as well, without the creation of temporary variables:
Client client = new Client(Integer.parseInt(tokens[0]), tokens[1], tokens[2], tokens[3], tokens[4], tokens[5], Integer.parseInt(tokens[6]));
This case brings us to the third solution with setters and getters.
The variables that describe the Client are already defined, it is possible to pass them to assemble the perfect object, but it is not possible to retrieve them. Instead of setting the variables directly in the constructor, we can create a special method that will do the job, for instance:
// Other fields omitted
private int clientID;
// The empty constructor required for later usage,
// since right now, we can't create the object without specifying every property.
public Client() {
}
// This method does exactly the same thing that was done before but
// in the constructor directly
public void setClientID(int clientID) {
this.clientID = clientID;
}
// This method will assist in retrieving the set data from above.
public int getClientID() {
return clientID;
}
And then the while loop would look like this instead:
Client client = new Client();
client.setClientID(Integer.parseInt(tokens[0]));
client.setFirstName(tokens[1]);
client.setSurName(tokens[2]);
client.setStreet(tokens[3]);
client.setSuburb(tokens[4]);
client.setState(tokens[5]);
client.setPostcode(Integer.parseInt(tokens[6]));
And to get those values:
System.out.println("Client ID: " + client.getClientID());
Or you could use the constructor with the fields to create the client, add getters in the class, omit both setters, and the empty constructor if the creation of the client should only be possible with all the fields present.

Why do I get, and how do I solve this "String to object of type <objecttype>" error

I am (being an absolute beginner), trying to create a simple tool, that creates some objects and links them.
The objects are:
Customers
Licenses (2 types, extends class)
The idea is to use (one of) the customer company name when creating a license, so the license is linked to a customer.
I use ArrayLists to store the data.
I tried to use the getter for Customer cCompany, but when I try to actually create a new license object, I get errors about incompatible types (String to object of type customer)
How can I fix that error?
Any help is highly appreciated, but please explain well, me being an absolute beginner. I probably overcomplicate stuff....
Some code extracts:
From Main:
public class Main {
public static void main(String[] args) {
//Create customers
List <Customer> customers = new ArrayList <> (10);
customers.add(new Customer("TestCompany","John Doe",1234567890,"John#testcompany.com"));
....
//Create Elvis licenses (based on superclass License)
List <ElvisLicense> ellicenses = new ArrayList <> (10);
ellicenses.add(new ElvisLicense("TestCompany","VendorA",1234,"1234-A","Solutions Server gold","1234-dtbk-87654-nlof",10, true , true));
Class: Customer:
class Customer {
String cCompany;
private String cName;
private int cPhone;
private String cEmail;
public Customer( String cCompany, String cName,int cPhone, String cEmail)
{
this.cCompany = cCompany;
this.cName = cName;
this.cPhone = cPhone;
this.cEmail = cEmail;
}
//This getter should be used to link the license to the customer (Done in License.java)
public String getcCompany() {
return cCompany;
}
Class License (Superclass)
class License {
// Used no modifier to set access for Class/Package and Subclass inside the package
Customer licenseCompany;
String lVendor;
int lContractNumber;
String lCertificateNumber;
String lProductName;
String lLicenseKey;
int lNumberOfSeats;
public License(Customer cCompany, String lVendor, int lContractNumber, String lCertificateNumber,
String lProductName, String lLicenseKey, int lNumberOfSeats)
{
licenseCompany = cCompany;
this.lVendor = lVendor;
this.lVendor = lVendor;
this.lContractNumber = lContractNumber;
this.lCertificateNumber = lCertificateNumber;
this.lProductName = lProductName;
this.lLicenseKey = lLicenseKey;
this.lNumberOfSeats = lNumberOfSeats;
}
public Customer getLicenseCompany() {
return licenseCompany;
}
public void setLicenseCompany(Customer licenseCompany) {
this.licenseCompany = licenseCompany;
}
//preparations to allow for example printing the content of an arraylist element
#Override
public String toString(){
return "Customer name " + getLicenseCompany() + "\n" + "Vendor name " + getlVendor() + "\n" + "Contract number: " + getlContractNumber() + "\n"
+ "Certificate number: " + getlCertificateNumber() + "\n" +
"Product name " + getlProductName() + "\n" + "Licence key: " + getlLicenseKey() + "\n"
+ "Number of seats: " + getlNumberOfSeats();
}
}
And the extended class:
public class ElvisLicense extends License{
private boolean elIsBundle;
private boolean elIsSubscription;
public ElvisLicense(
Customer licenseCompany,
String lVendor,
int lContractNumber,
String lCertificateNumber,
String lProductName,
String lLicenseKey,
int lNumberOfSeats,
boolean elIsBundle,
boolean elIsSubscription
)
{
super(
licenseCompany,
lVendor,
lContractNumber,
lCertificateNumber,
lProductName,
lLicenseKey,
lNumberOfSeats);
this.elIsBundle = elIsBundle;
this.elIsSubscription = elIsSubscription;
}
.....
#Override
public String toString(){
return "Customer name " + licenseCompany + "\n"
+ "Vendor name " + lVendor + "\n"
+ "Contract number: " + lContractNumber + "\n"
+ "Certificate number: " + lCertificateNumber + "\n"
+ "Product name " + lProductName + "\n"
+ "Licence key: " + lLicenseKey + "\n"
+ "Number of seats: " + lNumberOfSeats + "\n"
+ "Number of seats: " + elIsBundle + "\n"
+ "Number of seats: " + elIsSubscription;
}
}
I expect that the Customername is used when creating a new license.
Below line is wrong.
ellicenses.add(new ElvisLicense("TestCompany","VendorA",1234,"1234-A","Solutions Server gold","1234-dtbk-87654-nlof",10, true , true));
As license need customer object an parameter. Instead, you should create customer object first.
ellicenses.add(new ElvisLicense(new Customer("TestCompany","VendorA",1234,"1234-A"),"Solutions Server gold","1234-dtbk-87654-nlof",10, true , true));
for reusing that customer list to avoid create company.
for(Customer customer : customers){
// here you need some way to offer other parameters except customer parameter.
License license = new new ElvisLicense(customer,"Solutions Server gold","1234-dtbk-87654-nlof",10, true , true);
ellicenses.add(license);
}
What you need to do is to use one of the Customer objects you have already created when creating the ElvisLicense object. To more easily find that customer by name I suggest you store them in a map instead of a list with the name as a key.
Map<String, Customer> customerMap = new HashMap<>();
Customer customer = new Customer("TestCompany","John Doe",1234567890,"John#testcompany.com"));
customerMap.put(customer.getcCompany(), customer);
so when creating the license you look up the customer
List <ElvisLicense> ellicenses = new ArrayList <> (10);
Customer customer = customerMap.get("TestCompany");
if (customer != null) {
ElvisLicense license = new ElvisLicense(customer,"VendorA",1234,"1234-A","Solutions Server gold","1234-dtbk-87654-nlof",10, true , true));
ellicenses.add(license);
} else {
//If the customer isn't found you need some kind of error handling, better than below :)
System.out.println("Can't create a license, no customer found");
}

Jackson does not store xml values in object

I have an XML file like this:
"<?xml version ='1.0' encoding='UFT-8'?>"+
"<CLOSE_TRAILER>"+
"<user>" +
"<id>0600580</id>" +
"<trans_id>7</trans_id>" +
"<host_name>Mnull</host_name>"+
"<session_token>79430831Zahyy</session_token>" +
"<time_stamp>1551736026682</time_stamp>" +
"</user>"+
"<trailer_detail>" +
"<org_id>432</org_id>" +
"<trailer_prefix>XTRA</trailer_prefix>" +
"<system_type>SCAN</system_type>" +
"<truck_mail_flag>false</truck_mail_flag>" +
"<trailer_type_code>T</trailer_type_code>" +
"<in_contingency_flag>false</in_contingency_flag>" +
"<detl_code_svc_type>OVN</detl_code_svc_type>" +
"<catg_code_svc_type></catg_code_svc_type>" +
"<seal_number>12345</seal_number>" +
"<user_id>5155035</user_id>" +
"<trailer_number>24813</trailer_number>" +
"</trailer_detail>"+
"<load_handler>" +
"<employee_id>TEST</employee_id>" +
"<trailer_load_seq>71373839</trailer_load_seq>" +
"<action_code></action_code>" +
"</load_handler>"+
"</CLOSE_TRAILER>";
I want to map the trailer_detail and load_handler but not user, it into this object that I created for this xml:
#JsonRootName(value = "CLOSE_TRAILER")
public class CloseTrailerXml {
#JsonUnwrapped
Trailer trailer_detail;
#JsonUnwrapped
LoadHandler load_handler;
}
The Trailer class I have mapped like this:
#JsonRootName("trailer_detail")
public class Trailer {
private Integer equipmentSeq;
private String createdBy;
private ZonedDateTime createdDt;
private String updatedBy;
private ZonedDateTime updatedDt;
#JsonProperty("trailer_number")
private String trlrNbr;
... more member variables
In a unit test case I have the above xml stored as a string and I use this code below to map it into the object:
String msgNoNewLineChar = originalScannerMsg.replace("\\n","");
String msgNoCarReturnChar = msgNoNewLineChar.replace("\\r", "");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.setSerializationInclusion(Include.NON_NULL);
CloseTrailerXml closeTrailerXml= xmlMapper.readValue(originalScannerMsg, CloseTrailerXml.class);
The closeTrailerXml is created but all values are null.
If I remove the encoding line and CLOSE_TRAILER tag and the user section, it maps fine.
Why can't the object mapper work with the user section present?

Looking for a Straightforward Way to Parse JSON

I am attempting to parse the following JSON using Java:
{ "student_id": "123456789", "student_name": "Bart Simpson", "student_absences": 1}
What is the simplest way to accomplish this. I tried doing it the way below but think there must be an easier way.
import org.json.*
JSONObject obj = new JSONArray("report");
for(int i = 0; I < arr.length(); i++){
String studentname =
arr.getJSONObject(i).getString("student_id");
arr.getJSONObject(i).getString("student_name");
arr.getJSONObject(i).getString("student_name");
}
There's Gson:
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
public class Main {
public static void main(String[] args) {
String json = "{ \"student_id\": \"123456789\", \"student_name\": \"Bart Simpson\", \"student_absences\": 1}";
Student student = new Gson().fromJson(json, Student.class);
System.out.println(student);
}
}
class Student {
#SerializedName("student_id")
String studentId;
#SerializedName("student_name")
String studentName;
#SerializedName("student_absences")
Integer studentAbsences;
#Override
public String toString() {
return "Student{" +
"studentId='" + studentId + '\'' +
", studentName='" + studentName + '\'' +
", studentAbsences=" + studentAbsences +
'}';
}
}
Another popular one is Jackson:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{ \"student_id\": \"123456789\", \"student_name\": \"Bart Simpson\", \"student_absences\": 1}";
Student student = new ObjectMapper().readValue(json, Student.class);
System.out.println(student);
}
}
class Student {
#JsonProperty("student_id")
String studentId;
#JsonProperty("student_name")
String studentName;
#JsonProperty("student_absences")
Integer studentAbsences;
#Override
public String toString() {
return "Student{" +
"studentId='" + studentId + '\'' +
", studentName='" + studentName + '\'' +
", studentAbsences=" + studentAbsences +
'}';
}
}
In both cases, running Main will print:
Student{studentId='123456789', studentName='Bart Simpson', studentAbsences=1}
EDIT
And without creating a Student class, you could give something like JsonPath a try.
The answer to your question depends on what you want to achieve. Your example would result in an array of strings. The previous answer results in a Java class with a property for each field. Another option is to put the values into a map.
If have written an encoder / decoder combination for this. Encoding is quite easy: use the keys and values of the map. The decoder (JSON string to map or anything else) requires a parser (best would be a tokenizer).

Categories