Parse CSV file to a generic class - Java generics - java

I'm trying to build up a generic method that parses a CSV file into an Object.
I think I'm quite close to reach the objective but I'm a bit stuck with java generics, I'm still learning it.
Now I'm stuck on my while cycle where I create the objects. I'm using jCSV to do the parsing for me. I'm following their documentation tutorial here.
I can't figure out how to set the beanClass bc = it.next(); because beanClass does not exist as a class on my project, compilation error: cannot find symbol - class beanClass
How can I fix this?
I know I could simply do a List<?> beanClassList = csvFileReader.readAll();
but the problem is that on the first line of each CSV file I've the class name to where that data belongs to. I get this exception, which makes sense:
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "Car"
My CSV files are something like this:
ClassName
value,value,value,value,value
value,value,value,value,value
...
Here's my code:
public String importFromCsvFile(File f) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
{
FileReader fr = new FileReader(f);
BufferedReader buffReader = new BufferedReader(fr);
String className = buffReader.readLine();
buffReader.close();
//Java reflection to get the Class Object.
Class beanClass = Class.forName("model." + className);
Object beanObject = beanClass.newInstance();
Reader reader = new FileReader(f);
ValueProcessorProvider provider = new ValueProcessorProvider();
CSVEntryParser<?> entryParser = new AnnotationEntryParser<>(beanClass, provider);
CSVReader<?> csvFileReader= new CSVReaderBuilder<>(reader).entryParser((CSVEntryParser<Object>) entryParser).build();
Iterator<?> it = csvFileReader.iterator();
while (it.hasNext()) {
beanClass bc = it.next(); // here is the compilation error
}
}
Here's a CSV file example:
Car
1,BMW,Z3,2000,20-AC-57
2,Mercedes,C4,2010,23-32-VJ
3,Alfa Romeo,A3,1992,XX-XX-XX

you are nearly there.
public String importFromCsvFile(File f) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
{
FileReader fr = new FileReader(f);
BufferedReader buffReader = new BufferedReader(fr);
String className = buffReader.readLine();
buffReader.close(); // you can also not close it and use buffReader as your reader for the CSV
//Java reflection to get the Class Object.
Class beanClass = Class.forName("model." + className);
Object beanObject = beanClass.newInstance(); // this is never needed
Reader reader = new FileReader(f); // use buffReader instead of creating a new one
ValueProcessorProvider provider = new ValueProcessorProvider();
CSVEntryParser<?> entryParser = new AnnotationEntryParser<>(beanClass, provider);
CSVReader<?> csvFileReader= new CSVReaderBuilder<>(reader).entryParser((CSVEntryParser<Object>) entryParser).build();
Iterator<?> it = csvFileReader.iterator();
while (it.hasNext()) {
Object obj = it.next(); // obj is an instance of Car with your data
boolean isCar = obj instanceof Car; // will be true
}
}
Because you are using , as separator you should consider using UK_DEFAULT as Strategy for the Reader or defining your own (the default separator is ;).
You should also either continue using the BufferedReader or specify skipHeader in the Strategy - else you Car will be treated as entry which is probably not what you want.

As per your comments Please have a look at sample code that reads data from CSV file and store in a map as key-value pair.
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
BufferedReader reader = new BufferedReader(new FileReader(new File("resources/abc.csv")));
String header = reader.readLine();
String[] keys = header.split(",");
String line = null;
while ((line = reader.readLine()) != null) {
Map<String, String> map = new HashMap<String, String>();
String[] values = line.split(",");
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
list.add(map);
}
reader.close();
for(Map<String, String> map:list){
for(String key:map.keySet()){
System.out.println(key+":"+map.get(key));
}
System.out.println();
}
CSV:
ID,NAME,MODEL,YEAR,NUMBER
1,BMW,Z3,2000,20-AC-57
2,Mercedes,C4,2010,23-32-VJ
3,Alfa Romeo,A3,1992,XX-XX-XX

With generics, types are specified at compile time (and checked by the compiler). In your case, types are only specified in the CSV file and thus unknown at compile time. So generics is not a tool you can use in this case.
What exactly would you want to accomplish with generics? What would you like the compiler to check?
What you can do is create an instance of the class (you will need a full name including package) using Class.forName(name).newInstance() and use reflection to set some properties. But at compile time you'll only know the result is an Object.

Related

Retrieving variables from a text file through a hashmap

I am creating a school program for a project in which I a writing words and translations to a file, (separated by a character). I have read that I can read them via a hash map into an array. I was just wondering if someone could point me in the right direction as how to do this.
If anyone has a better idea of how to store and retrieve the words I would love to learn. The reason I am writing to a file is so the user can store as many words as they want.
Thank-you so much :D
You can use java.util.HashMap to store user words and related translations:
String userWord = null;
String translation = null, translation1 = null;
Map<String, String[]> map = new HashMap();
map.put(userWord, new String[] { translation, translation1 });
String[] translations = map.get(userWord);
This map lets you map single userWord to multiple translations.
Here's a reference for learning how to use BufferedReader: BufferedReader
Here's a reference for learning how to use FileReader: FileReader
import java.io.*;
class YourClass
{
public static void main() throws IOException
{
File f = new File("FilePath"); // Replace every '\' with '/' in the file path
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String line = "";
String FileString = "";
while((line = br.readLine()) != null)
{
// Now 'line' contains each line of the file
// If you want, you can store the entire file in a String, like this:
FileString += line + "\n"; // '\n' to register each new line
}
System.out.println(FileString);
}
} // End of class
I'm still a newbie, and don't understand much about HashMap, but I can tell you how to store it in a String array:
FileString = FileString.replaceAll("\\s+", " ");
String[] Words = FileString.split(" ");
FileString.replaceAll("\\s+", " ") - Replaces 1 or more spaces with 1 space, so as to avoid any logical errors.
FileString.split(" ") - Returns a String array of each String separated by a space.
You can try something like this
File f = new File("/Desktop/Codes/Text.txt");
// enter your file location
HashMap<String, String[]> hs = new HashMap<String, String[]>();
// throw exception in main method
Scanner sc = new Scanner(f);
String s="";
while(sc.hasNext()){
s = sc.next();
// create a method to search translation
String []trans = searchTrans(s);
hs.put(s, trans);
}

Read Multiple JSON object from a Text File

My Question is similar to what has been asked here .
few points :
I can not change the format. (No commas to be added etc)
This is basically a huge .txt file containing 1000's of Json objects.
My Json objects are HUGE.
This is what I am doing right now :
FileReader fileReader = new FileReader(fileName);
BufferedReader reader = new BufferedReader(fileReader);
String data = "";
while((data = reader.readLine()) != null){
ObjectMapper mapper = new ObjectMapper();
Map<String,String> map = mapper.readValue(data, Map.class);
}
Currently I am using Jackson and Ideally I would like to read one Json Object from the file at a time, Parse it and then move on to the next one. I need to count let say unique number of id's from these Json object and do more operations. It will be best to read them one by one.
Is jackson would be the best way going forward ?
This is a good example of parsing huge Json, But it deals with only one object per file. My file has huge Jsons (1000s of them).
Here is a Jackson example that works for me. I have thousands json objects (tokens) in a single json file. This code will iterate through the file read each token and print it's serial.
Required imports:
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
Using Jackson to read multiple json objects from FileInputStream:
try (FileInputStream fis = new FileInputStream("D:/temp/tokens.json")) {
JsonFactory jf = new JsonFactory();
JsonParser jp = jf.createParser(fis);
jp.setCodec(new ObjectMapper());
jp.nextToken();
while (jp.hasCurrentToken()) {
Token token = jp.readValueAs(Token.class);
jp.nextToken();
System.out.println("Token serial "+token.getSerialNumber());
}
}
Here is a more JAVA 8ish solution for your query, I always lean toward BufferedReader over InputStreams for any place where parsing is going to be done a lot of time.
ObjectMapper mapper = new ObjectMapper();
JsonFactory jsonFactory = new JsonFactory();
try(BufferedReader br = new BufferedReader(new FileReader("luser.txt"))) {
Iterator<luser> value = mapper.readValues( jsonFactory.createParser(br), luser.class);
value.forEachRemaining((u)->{System.out.println(u);});
}
The deserialization for each object happens as part of next(), in each iteration.
Here is how I used Gson's JSONReader API to handle similar requirement as above
public static List<YOURPOJO> readTraceLog(String filepath) throws IOException {
Gson gson = new Gson();
JsonReader jsonReader = new JsonReader(new FileReader(filepath));
// important as handles unwanted formatting stuffs such empty spaces
jsonReader.setLenient(true);
boolean start = true; // start of read
jsonReader.beginObject(); // first object begins
//List to hold object
List<YOURPOJO> completeList = new ArrayList<YOURPOJO>();
//YOURPOJO has two attributes one is ID and other is list of ANOTHERPOJO
while (jsonReader.hasNext()) {
if (!start) {
//to stop end of Document
if (jsonReader.peek().toString().matches("END_DOCUMENT")) {
break;
}
//continue reading object as the come -{
jsonReader.beginObject();
}
start = false;
YOURPOJO pojo = new YOURPOJO();
//read attribute id
String name = jsonReader.nextName();
pojo.setId(name);
//list to store ANOTHERPOJO objects
List<ANOTHERPOJO> tempList = new ArrayList<ANOTHERPOJO>();
//begin reading list - [
jsonReader.beginArray();
while (jsonReader.hasNext()) {
ANOTHERPOJO t = gson.fromJson(jsonReader, ANOTHERPOJO.class);
tempList.add(t);
}
//end reading list - ]
jsonReader.endArray();
//store data
pojo.setTraceDetails(tempList);
completeList.add(YOURPOJO);
//end of object - }
jsonReader.endObject();
}
jsonReader.close();
return completeList;
}

Bean class into a list - Java Generics - Cannot find symbol

I'm using java generics to read a CSV file and create the respective object. I'm making it generic to accept any CSV file and map it to a class.
The first line in my CSV as the class name, then I will read the remaining lines and build an object for each line.
I'm using the CSVReader to help me doing it. But I get compilation errors (I marked them in the code).
Here's my method:
public String importFromCsvFile(File f) throws IOException, ClassNotFoundException
{
FileReader fr = new FileReader(f);
BufferedReader reader = new BufferedReader(fr);
String className = reader.readLine();
//Java reflection to get the Class Object.
Class BeanClass = Class.forName(className);
// Reader reader = new FileReader("persons.csv");
ValueProcessorProvider provider = new ValueProcessorProvider();
CSVEntryParser<BeanClass> entryParser = new AnnotationEntryParser<BeanClass>(BeanClass.class, provider); // BeanClass, cannot find symbol
CSVReader<BeanClass> csvPersonReader = new CSVReaderBuilder<BeanClass>(reader).entryParser(entryParser).build(); // BeanClass, cannot find symbol
List<BeanClass> beanClassList = csvPersonReader.readAll(); // BeanClass, cannot find symbol
System.out.println("First line: " + className);
return "";
}
I'm still building up my method but with this compiling errors I can not move on. Does anyone knows how to fix them?
Again, BeanClass can be any data class I've, so it has to be generic.
Thanks!
You're confusing static class names, known at compile time (like String, or ValueProcessorProvider) with variables referencing a class, only known at runtime. It would be easier to distinguish the two if you respected the Java naming conventions: class names start with upperccase latters, and variables with lowercase letters. So the code should be
FileReader fr = new FileReader(f);
BufferedReader reader = new BufferedReader(fr);
String className = reader.readLine();
//Java reflection to get the Class Object.
Class beanClass = Class.forName(className);
ValueProcessorProvider provider = new ValueProcessorProvider();
CSVEntryParser<beanClass> entryParser = new AnnotationEntryParser<beanClass>(beanClass.class, provider); // BeanClass, cannot find symbol
CSVReader<beanClass> csvPersonReader = new CSVReaderBuilder<beanClass>(reader).entryParser(entryParser).build(); // beanClass, cannot find symbol
List<beanClass> beanClassList = csvPersonReader.readAll(); // BeanClass, cannot find symbol
System.out.println("First line: " + className);
return "";
Now you can immediately see that you're using a variable, beanClass, in places where only literal class names, known at compile-time, can be used: what you must have between <> is a literal class name, not a variable, since it's used by the compiler to ensure type safety. I haven't checked that the following code compiles, but it should be closer than your attempt:
FileReader fr = new FileReader(f);
BufferedReader reader = new BufferedReader(fr);
String className = reader.readLine();
//Java reflection to get the Class Object.
Class<?> beanClass = Class.forName(className);
ValueProcessorProvider provider = new ValueProcessorProvider();
CSVEntryParser<?> entryParser = new AnnotationEntryParser<>(beanClass, provider);
CSVReader<?> csvPersonReader = new CSVReaderBuilder<>(reader).entryParser(entryParser).build();
List<?> beanClassList = csvPersonReader.readAll();
System.out.println("First line: " + className);
return "";
Looking to the class I think that BeanClass is no real class. If a class does not exist, compiler cannot compile it. You shall provide your class (bean) to use. You shall implement
public class BeanClass {
#MapToColumn(column=0)
private String firstColumn;
See http://code.google.com/p/jcsv/wiki/ValueProcessorExample.

Compare values in two files

I have two files Which should contain the same values between Substring 0 and 10 though not in order. I have Managed to Outprint the values in each file but I need to Know how to Report say id the Value is in the first File and Notin the second file and vice versa. The files are in these formats.
6436346346....Other details
9348734873....Other details
9349839829....Other details
second file
8484545487....Other details
9348734873....Other details
9349839829....Other details
The first record in the first file does not appear in the second file and the first record in the second file does not appear in the first file. I need to be able to report this mismatch in this format:
Record 6436346346 is in the firstfile and not in the secondfile.
Record 8484545487 is in the secondfile and not in the firstfile.
Here is the code I currently have that gives me the required Output from the two files to compare.
package compare.numbers;
import java.io.*;
/**
*
* #author implvcb
*/
public class CompareNumbers {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
File f = new File("C:/Analysis/");
String line;
String line1;
try {
String firstfile = "C:/Analysis/RL001.TXT";
FileInputStream fs = new FileInputStream(firstfile);
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
while ((line = br.readLine()) != null) {
String account = line.substring(0, 10);
System.out.println(account);
}
String secondfile = "C:/Analysis/RL003.TXT";
FileInputStream fs1 = new FileInputStream(secondfile);
BufferedReader br1 = new BufferedReader(new InputStreamReader(fs1));
while ((line1 = br1.readLine()) != null) {
String account1 = line1.substring(0, 10);
System.out.println(account1);
}
} catch (Exception e) {
e.fillInStackTrace();
}
}
}
Please help on how I can effectively achieve this.
I think I needed to say that am new to java and may not grab the ideas that easily but Am trying.
Here is the sample code to do that:
public static void eliminateCommon(String file1, String file2) throws IOException
{
List<String> lines1 = readLines(file1);
List<String> lines2 = readLines(file2);
Iterator<String> linesItr = lines1.iterator();
while (linesItr.hasNext()) {
String checkLine = linesItr.next();
if (lines2.contains(checkLine)) {
linesItr.remove();
lines2.remove(checkLine);
}
}
//now lines1 will contain string that are not present in lines2
//now lines2 will contain string that are not present in lines1
System.out.println(lines1);
System.out.println(lines2);
}
public static List<String> readLines(String fileName) throws IOException
{
List<String> lines = new ArrayList<String>();
FileInputStream fs = new FileInputStream(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(fs));
String line = null;
while ((line = br.readLine()) != null) {
String account = line.substring(0, 10);
lines.add(account);
}
return lines;
}
Perhaps you are looking for something like this
Set<String> set1 = new HashSet<>(FileUtils.readLines(new File("C:/Analysis/RL001.TXT")));
Set<String> set2 = new HashSet<>(FileUtils.readLines(new File("C:/Analysis/RL003.TXT")));
Set<String> onlyInSet1 = new HashSet<>(set1);
onlyInSet1.removeAll(set2);
Set<String> onlyInSet2 = new HashSet<>(set2);
onlyInSet2.removeAll(set1);
If you guarantee that the files will always be the same format, and each readLine() function is going to return a different number, why not have an array of strings, rather than a single string. You can then compare the outcome with greater ease.
Ok, first I would save the two sets of strings in to collections
Set<String> s1 = new HashSet<String>(), s2 = new HashSet<String>();
//...
while ((line = br.readLine()) != null) {
//...
s1.add(line);
}
Then you can compare those sets and find elements that do not appear in both sets. You can find some ideas on how to do that here.
If you need to know the line number as well, you could just create a String wrapper:
class Element {
public String str;
public int lineNr;
public boolean equals(Element compElement) {
return compElement.str.equals(str);
}
}
Then you can just use Set<Element> instead.
Open two Scanners, and :
final TreeSet<Integer> ts1 = new TreeSet<Integer>();
final TreeSet<Integer> ts2 = new TreeSet<Integer>();
while (scan1.hasNextLine() && scan2.hasNexLine) {
ts1.add(Integer.valueOf(scan1.nextLigne().subString(0,10));
ts1.add(Integer.valueOf(scan1.nextLigne().subString(0,10));
}
You can now compare ordered results of the two trees
EDIT
Modified with TreeSet
Put values from each file to two separate HashSets accordingly.
Iterate over one of the HashSets and check whether each value exists in the other HashSet. Report if not.
Iterate over other HashSet and do same thing for this.

csv parser reading headers

I'm working on a csv parser, I want to read headers and the rest of the csv file separately.
Here is my code to read csv.
The current code reads everything in the csv file, but I need to read headers separate.
please help me regarding this.
public class csv {
private void csvRead(File file)
{
try
{
BufferedReader br = new BufferedReader( new FileReader(file));
String strLine = "";
StringTokenizer st = null;
File cfile=new File("csv.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(cfile));
int tokenNumber = 0;
while( (strLine = br.readLine()) != null)
{
st = new StringTokenizer(strLine, ",");
while(st.hasMoreTokens())
{
tokenNumber++;
writer.write(tokenNumber+" "+ st.nextToken());
writer.newLine();
}
tokenNumber = 0;
writer.flush();
}
}
catch(Exception e)
{
e.getMessage();
}
}
We have withHeader() method available in CSVFormat. If you use this option then you will be able to read the file using headers.
CSVFormat format = CSVFormat.newFormat(',').withHeader();
Map<String, Integer> headerMap = dataCSVParser.getHeaderMap();
will give you all headers.
public class CSVFileReaderEx {
public static void main(String[] args){
readFile();
}
public static void readFile(){
List<Map<String, String>> csvInputList = new CopyOnWriteArrayList<>();
List<Map<String, Integer>> headerList = new CopyOnWriteArrayList<>();
String fileName = "C:/test.csv";
CSVFormat format = CSVFormat.newFormat(',').withHeader();
try (BufferedReader inputReader = new BufferedReader(new FileReader(new File(fileName)));
CSVParser dataCSVParser = new CSVParser(inputReader, format); ) {
List<CSVRecord> csvRecords = dataCSVParser.getRecords();
Map<String, Integer> headerMap = dataCSVParser.getHeaderMap();
headerList.add(headerMap);
headerList.forEach(System.out::println);
for(CSVRecord record : csvRecords){
Map<String, String> inputMap = new LinkedHashMap<>();
for(Map.Entry<String, Integer> header : headerMap.entrySet()){
inputMap.put(header.getKey(), record.get(header.getValue()));
}
if (!inputMap.isEmpty()) {
csvInputList.add(inputMap);
}
}
csvInputList.forEach(System.out::println);
} catch (Exception e) {
System.out.println(e);
}
}
}
Please consider the use of Commons CSV. This library is written according RFC 4180 - Common Format and MIME Type for Comma-Separated Values (CSV) Files. What is compatible to read such lines:
"aa,a","b""bb","ccc"
And the use is quite simple, there is just 3 classes, and a small sample according documentation:
Parsing of a csv-string having tabs as separators, '"' as an optional
value encapsulator, and comments starting with '#':
CSVFormat format = new CSVFormat('\t', '"', '#');
Reader in = new StringReader("a\tb\nc\td");
String[][] records = new CSVParser(in, format).getRecords();
And additionally you get this parsers already available as constants:
DEFAULT - Standard comma separated format as defined by RFC 4180.
EXCEL - Excel file format (using a comma as the value delimiter).
MYSQL - Default MySQL format used by the SELECT INTO OUTFILE and LOAD DATA INFILE operations.
TDF - Tabulation delimited format.
Have you considered OpenCSV?
Previous question here...
CSV API for Java
Looks like you can split out the header quite easily...
String fileName = "data.csv";
CSVReader reader = new CSVReader(new FileReader(fileName ));
// if the first line is the header
String[] header = reader.readNext();
// iterate over reader.readNext until it returns null
String[] line = reader.readNext();
Your code here, being
while( (strLine = br.readLine()) != null)
{
//reads everything in your csv
}
will print all of your CSV content.
For example, the following fetches your header:
Reader in = ...;
CSVFormat.EXCEL.withHeader("Col1", "Col2", "Col3").parse(in);
As suggested, life could be easier using the predefined CSVFormat from the apache commons library. Link here (https://commons.apache.org/proper/commons-csv/user-guide.html).
Cheers.

Categories