I am new to Java and practice parsing csv file. I do understand what does IndexOutOfBound means, but I don't understand why my parsed data cannot do like all the tutorials I've visited such as https://examples.javacodegeeks.com/java-csv-parsing-example/
I can only read the first column, which is data[0]. There must be something wrong with my parsing method, but I cannot figure it out. Any help or hint is hight appreciated.
my data file looks like this:
[0], [1], [2], [3] , [4] , [5] , [6] , [7] , [8] , [9]
class, gender, age, bodyType, profession, pregnant, isYou ,species, isPet, role
scenario:green, , , , , , , ,
person, female, 24, average , doctor , FALSE , , , , passenger
animal, male , 4, , , FALSE , , dog , TRUE , pedestrian
.
.
I tried to parse like this:
ArrayList<String> csvContents = new ArrayList<String>();
try (BufferedReader csvReader = new BufferedReader(new FileReader(csvFile));) {
String headerLine = csvReader.readLine(); //get rid of header
while ((line = csvReader.readLine()) != null) {
csvContents.add(line);// add the line to the ArrayList
}
for (String csvLine : csvContents) {
// split by comma and remove redundant spaces
String[] data = csvLine.split("\\s*,\\s*");
System.out.println(data[1]);// IndexOutOfBound
Character character = null;
String clazz = data[0].toLowerCase();// cannot use word "class" as a variable
Profession professionEnum = Profession.valueOf(data[4].toUpperCase());
Gender genderEnum = Gender.valueOf(data[1].toUpperCase());
BodyType bodyTypeEnum =BodyType.valueOf(data[3].toUpperCase());
if (clazz.startsWith("scenario")) {
scenario = new Scenario();
scenario.setLegalCrossing(clazz.endsWith("green"));
continue;
} else if ("person".equals(clazz)) {
person = new Person(Integer.parseInt(data[2]), professionEnum ,genderEnum , bodyTypeEnum , Boolean.parseBoolean(data[5]));
person.setAsYou(Boolean.parseBoolean(data[6]));
} else if ("animal".equals(clazz)) {
animal = new Animal(Integer.parseInt(data[2]) , genderEnum , bodyTypeEnum, data[7]);
animal.setIsPet(Boolean.parseBoolean(data[8]));
}
} catch (someException e) {
e.printStackTrace();
}
EDIT
print out csvLine before split:
scenario:green,,,,,,,,,
person,female,24,average,doctor,false,false,,,passenger
person,male,40,overweight,unknown,false,false,,,passenger
person,female,2,average,,false,false,,,passenger
person,male,82,average,,false,false,,,pedestrian
person,female,32,average,ceo,true,false,,,pedestrian
person,male,7,athletic,,false,false,,,pedestrian
animal,male,4,,,false,false,dog,true,pedestrian
scenario:red,,,,,,,,,
After spliting, the data just have one element, so that when you access data[1], then you get exception.
Solution: try with another regex like "," only.
Ps: your csv is malformed at
scenario:green, , , , , , , ,
Try to put one more ","
you need to fill comlete data for all cells in a row.
For eg. first line in your csv, contains only 1 cell having value scenario:green which is data[0].
If you fill in data for all other cells in your csv, your will start receiving data[1], data[2], data[3]....
I've figured it out. It's counterintuitive for me, though. I need to specify the length of the data array parsed to put every attribute like this:
ArrayList<String> csvContents = new ArrayList<String>();
try (BufferedReader csvReader = new BufferedReader(new FileReader(csvFile));) {
String headerLine = csvReader.readLine(); //get rid of header
while ((line = csvReader.readLine()) != null) {
csvContents.add(line);// add the line to the ArrayList
}
for (String csvLine : csvContents) {
// split by comma and remove redundant spaces
String[] data = csvLine.split("\\s*,\\s*");
System.out.println(data[1]);// IndexOutOfBound
Character character = null;
String clazz = data[0].toLowerCase();// cannot use word "class" as a variable
if (clazz.startsWith("scenario"&& data.length == 1)) {
scenario = new Scenario();
scenario.setLegalCrossing(clazz.endsWith("green"));
continue;
} else if ("person".equals(clazz)&& data.length == 10) {
Profession professionEnum = Profession.valueOf(data[4].toUpperCase());
Gender genderEnum = Gender.valueOf(data[1].toUpperCase());
BodyType bodyTypeEnum =BodyType.valueOf(data[3].toUpperCase());
person = new Person(Integer.parseInt(data[2]), professionEnum ,genderEnum , bodyTypeEnum , Boolean.parseBoolean(data[5]));
person.setAsYou(Boolean.parseBoolean(data[6]));
} else if ("animal".equals(clazz)) {
animal = new Animal(Integer.parseInt(data[2]) , genderEnum , bodyTypeEnum, data[7]);
animal.setIsPet(Boolean.parseBoolean(data[8]));
}
} catch (someException e) {
e.printStackTrace();
}
Related
I am creating an endpoint that, gets parameters:
a CSV file
name of a class (that I will use to create an instance of)
In mapping I am using CommonsCSV library.
Although I am getting CSVRecord I couldn't map it to an instance of the class I got as a parameter.
Example:
as A parameter I got:-
CSV
id
firstName
lastName
1
John
Rodriguez
2
Michael
Hernandez
3
David
Smith
className
'employee'
// employee class
Class<?> classType = Class.forName(className);
// CSV file records
List<CSVRecord> records = csvParser.getRecords();
for (int i = 0; i < records.size(); i++) {
CSVRecord record = records.get(i);
// I want to get an instance of employee to save it to database like this
{
"id" : 1,
"firstName" : "John",
"lastName" : "Rodriguez"
}
}
Thanks in advance!
I think Super CSV can help you, detail you can check this doc. Super CSV
private static void readWithCsvBeanReader() throws Exception {
ICsvBeanReader beanReader = null;
try {
beanReader = new CsvBeanReader(new FileReader(CSV_FILENAME), CsvPreference.STANDARD_PREFERENCE);
// the header elements are used to map the values to the bean (names must match)
final String[] header = beanReader.getHeader(true);
final CellProcessor[] processors = getProcessors();
CustomerBean customer;
while( (customer = beanReader.read(CustomerBean.class, header, processors)) != null ) {
System.out.println(String.format("lineNo=%s, rowNo=%s, customer=%s", beanReader.getLineNumber(),
beanReader.getRowNumber(), customer));
}
}
finally {
if( beanReader != null ) {
beanReader.close();
}
}
}
I am new to Java and practicing parsing csv file into the object. I've tried but cannot figure it out.
The file looks like this:
[0], [1], [2], [3] , [4] , [5] , [6] , [7] , [8] , [9]
class, gender, age, bodyType, profession, pregnant, isYou ,species, isPet, role
scenario:green, , , , , , , ,
person, female, 24, average , , FALSE , , , , passenger
animal, male , 4, , , FALSE , , dog , TRUE , pedestrian
scenario:red
person, male , 16, athletic, boxer , FALSE , TRUE , , , passenger
person, female, 25, athletic, doctor , TRUE , FALSE , , , pedestrian
I need to parse it by any number of passengers and pedestrians with any scenarios. Finally, add these scenarios into an ArrayList for analyzing.
What I think is to:
loop through each line, stops when reaches to the next scenario:red, adds the passengers and the pedestrians to the Character ArrayList. (I've done adding, but don't how to stop).
Create a scenario using constructor scenario(ArrayList<Character> passenger, ArrayList<Character> pedestrians, boolean redOrGreen);
The ArrayList scenarios add the created scenarios.
What I've done is put everything together instead of separate them. Any help or hint is highly appreciated.
Thanks for this community who helped me, here is what I've got so far.
public void loadCsv() throws IOException {
String csvFile = "config.csv";
String line = "";
String csvSplit = "\\s*,\\s*";
Scenario scenario = new Scenario();
Person person = new Person();
Animal animal = new Animal();
ArrayList<Scenario> scenaios = new ArrayList<Scenario>();
ArrayList<String> csvContents = new ArrayList<String>();
ArrayList<Character> passengers = new ArrayList<Character>();
ArrayList<Character> pedestrians = new ArrayList<Character>();
try (BufferedReader csvReader = new BufferedReader(new FileReader(csvFile));) {
String headerLine = csvReader.readLine(); //get rid of the header
//add each line to the arrayList
while ((line = csvReader.readLine()) != null) {
csvContents.add(line);
}
for(String csvLine : csvContents) {
String[] data = csvLine.split(csvSplit); // split by comma and remove redundant spaces
if (data.length == NO_OF_FIELD) { //check and avoid indexOutOfBoundException
String clazz = data[0].toLowerCase();// cannot use word "class" as a variable
if (clazz.startsWith("scenario") && data.length == 1) {
scenario = new Scenario();
scenario.setLegalCrossing(clazz.endsWith("green"));
continue;
}
else if ("person".equals(clazz) && data.length ==10) {
person = loadCsvPerson(data);
addCharacter(person, data);
}
else if ("animal".equals(clazz) && data.length ==10) {
animal = loadCsvAnimal(data);
addCharacter(animal, data);
}
}
}
}
//passenger and pedestrians are in position
System.out.println("passengers: " + passengers);
System.out.println("pedestrians: " + pedestrians);
if (null != scenario) {
scenario.setPassengers(passengers);
scenario.setPedestrians(pedestrians);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
If it is possible to change the csv file format, I would add scenario type column (and scenario id or name if required), so you can work with csv file as a result set from database when you join tables (scenario + passenger + pedestrian) and return plain rows.
With this approach you will be able to delegate parsing to any csv library and do your logic (group by scenario id/name/type) separately. With surrogate rows you have (scenario:green...) you have to write your custom parser.
For example, you can use univocity to simply parse file into your model (even using annotations) and iteratively group it and handle.
Or if you need to work with existing file format do something like that:
if (clazz.startsWith("scenario") && data.length == 1) {
// collect existing scenario before starting processing new one
if (scenario != null) {
scenario.setPassengers(passengers);
scenario.setPedestrians(pedestrians);
passengers = new ArrayList();
pedestrians = new ArrayList();
scenarios.add(scenario);
}
// now start new group (scenario)
scenario = new Scenario();
scenario.setLegalCrossing(clazz.endsWith("green"));
continue;
}
Following things need to be addressed in your code:
Strive to avoid using the name of a class which is already used by the standard library (and especially when it is in the default package, java.lang) e.g. there is already a class Character in Java library and therefore you should use a different name for your custom class.
Use continue to skip the line, scenario:red
for(String csvLine : csvContents) {
if(csvLine.equals("scenario:red")){
continue;
}
String[] data = csvLine.split(csvSplit); // split by comma and remove redundant spaces
if (data.length == NO_OF_FIELD) {
//..
}
//..
}
If you have already defined final int NO_OF_FIELD = 10, you can use the same instead of using the value 10 directly i.e. you should use NO_OF_FIELD instead of 10 in the following code:
if (data.length == NO_OF_FIELD) { //check and avoid indexOutOfBoundException
String clazz = data[0].toLowerCase();// cannot use word "class" as a variable
//...
else if ("person".equals(clazz) && data.length ==10) {
However, you also need to understand that && data.length ==10 is unnecessary here as you have already checked data.length == NO_OF_FIELD in the enclosing if condition.
I couldn't understand the rest of your points. If you clarify them, I'll be able to help you further.
I need to add the previous scenario in the second round.
Since the last set of data won't be captured, I need to set another new scenario to add it in. Thanks for the art sir.
Character character = null;
try (BufferedReader csvReader = new BufferedReader(new FileReader(csvFile));) {
String headerLine = csvReader.readLine(); //get rid of the header
//add each line to the arrayList
while ((line = csvReader.readLine()) != null) {
csvContents.add(line);
}
final int NO_OF_FIELDS = 10;
for(String csvLine : csvContents) {
String[] data = csvLine.split(csvSplit); // split by comma and remove redundant spaces
String clazz = data[0].toLowerCase();// cannot use word "class" as a variable
if (clazz.startsWith("scenario") && data.length == 1) {
// adding scenario after one set of data
// i.e second round adding the first round data
if (passengers.size() != 0 && pedestrians.size() != 0) {
Scenario scenario = new Scenario();
scenario.setPassengers(passengers);
scenario.setPedestrians(pedestrians);
scenarios.add(scenario);
}
passengers = new ArrayList<Character>();
pedestrians = new ArrayList<Character>();
if (clazz.endsWith("green")) {
scenario.setLegalCrossing(true);
System.out.println("green light");
}
else if (clazz.endsWith("red")){
scenario.setLegalCrossing(false);
System.out.println("red light");
}
continue;
}
//...
Scenario scenario = new Scenario();
scenario.setPassengers(passengers);
scenario.setPedestrians(pedestrians);
scenarios.add(scenario);
scenario.setPassengers(passengers);
scenario.setPedestrians(pedestrians);
Audit audit = new Audit();
audit.setScenario(scenarios);
Getting only last category name instead of all unique categories into TextView
Set<String> uniqueCategories; // global
......
uniqueCategories = new TreeSet<>();
for(Checkout c : checkOutArrayList) {
uniqueCategories.add(c.getCategory());
}
for (String strGlobalCategory : uniqueCategories) {
System.out.println("Unique:"+strGlobalCategory);
textVisible.setText(strGlobalCategory); // getting name of last Category only
}
And when i am using textVisitCharges.setText(strGlobalCategory); out of for each loop getting strGlobalCategory cannot be resolved to a variable
You should append all categories to a single String and assign the result to the TextView :
StrinbBuilder text = new StringBuilder();
boolean first = true;
for (String strGlobalCategory : uniqueCategories) {
if (!first) {
text.append(", ");
}
first = false;
text.append (strGlobalCategory);
}
textVisible.setText(text.toSTring());
setText always replace old text with current text.
You can use append for see all categories.
like
for (String strGlobalCategory : uniqueCategories) {
System.out.println("Unique:"+strGlobalCategory);
textVisible.append(strGlobalCategory+", "); // Now you will have all categories
}
Set<String> uniqueCategories; // global
//Suppose your set of string is like that
String checkOutArrayList[]={"c","e","i","f","a","g","b","d","j","h"};
uniqueCategories = new TreeSet<String>();
for(String c : checkOutArrayList) {
uniqueCategories.add(c);
}
StringBuilder sb=new StringBuilder();
sb.append("");//This blank text does not show null when set is empty
for (String strGlobalCategory : uniqueCategories) {
sb.append(strGlobalCategory);
//sb.append(strGlobalCategory+" ");
//sb.append(strGlobalCategory+"\n");
//sb.append(strGlobalCategory+"\t");
}
//If you want to write the text without space your string is abcdefghij
//If you want to write the text with space your string is a b c d e f g h i j
//If you want to write the text with next line your string is
/*
a
b
c
...
*/
//If you want to write the text with tab your string is a b c d e f......
//In this way you can set all the set element with different ways...
textVisible.setText(sb);
//Your all set element is not appendign because the value is updated each time and the is showing
I have a method getFormattedValue(List<String> dataHeaders, List<String> data) that returns String value based contents of dataHeaders and data list. dataHeaders list can have 10 distinct values and based on value at particular index output string formation changes.
Below code was working fine tiil the time when dataHeaders contents are received in specific order , but lately this order is changing many atimes. As this input is received from other app/ system, i do not have control over order of elements. Hence i want to update my code so that it works correctly even after input order is altered.
If 1'st element of dataHeaders list is "OPERATION_NAME" then i need not take any action, but if "OPERATION_NAME" comes at 2'nd index in dataHeaders list then i need to do special formatting to output value. And so on....
My Pain is that i receive 10 dataHeaders. Please suggest me any good approach to handle this issue. I am posting my code below, suggestions are welcome.
import java.util.ArrayList;
import java.util.List;
public class ValueGenerator {
public String getFormattedValue(List<String> dataHeaders, List<String> data){
String formattedOutValue=null;
if(dataHeaders!=null && data!=null &&
dataHeaders.size() == data.size()){
if(dataHeaders.get(0).equals("OPERATION_NAME")){
formattedOutValue=data.get(0); // Add no spaces
}else if(dataHeaders.get(1).equals("OPERATION_NAME")){
formattedOutValue=data.get(1)+" "; // Add 4 blank spaces
}else if(dataHeaders.get(2).equals("OPERATION_NAME")){
formattedOutValue=data.get(2)+" "; // Add 6 blank spaces
}
}
//likewise i want to avoid redundant if - else if check
return formattedOutValue;
}
}
I assume that you want data.get(2) in the case of dataHeaders.get(2) and not data.get(1)
Basically you just need to find the index of the string OPERATION_NAME in dataHeaders and use it for data. The simplest way is to iterate over the list dataHeaders with an index and stop when you find it.
If you need to handle more strings then you could use a Map to map the strings you want to evaluate to their indices, this could look like this:
final static Map<Integer, String> formatByIndexMap;
static {
formatByIndexMap = new HashMap<>();
formatByIndexMap.put(0, "");
formatByIndexMap.put(1, " ");
formatByIndexMap.put(2, " ");
// and so on
formatByIndexMap.put(9, "----format for 10----");
}
public String getFormattedValue(List<String> dataHeaders, List<String> data){
String formattedOutValue = null;
if (dataHeaders != null && data != null &&
dataHeaders.size() == data.size()) {
Map<String, Integer> dhm = new HashMap<>();
for(int i = 0; i < dataHeaders.size(); i++) {
dhm.put(dataHeaders.get(i), i);
}
Integer operationNameIndex = dhm.get("OPERATION_NAME");
if(operationNameIndex != null) {
formattedOutValue = data.get(operationNameIndex.intValue()) +
formatByIndexMap.get(operationNameIndex);
}
}
return formattedOutValue;
}
The format map formatByIndexMap is defines as final static.
An for some basic testing:
#Test
public void getFormattedValue() {
List<String> data = Arrays.asList("operation", "foo", "bar");
List<String> dataHeaders;
String formated;
dataHeaders = Arrays.asList("OPERATION_NAME", "FOO_NAME", "BAR_NAME");
formated = getFormattedValue(dataHeaders, data);
Assert.assertEquals("operation", formated); // no spaces
dataHeaders = Arrays.asList("FOO_NAME", "OPERATION_NAME", "BAR_NAME");
formated = getFormattedValue(dataHeaders, data);
Assert.assertEquals("foo ", formated); // four spaces
dataHeaders = Arrays.asList("FOO_NAME", "BAR_NAME", "OPERATION_NAME");
formated = getFormattedValue(dataHeaders, data);
Assert.assertEquals("bar ", formated); // six spaces
dataHeaders = Arrays.asList("FOO_NAME", "BAR_NAME", "", "", "", "", "",
"", "", "OPERATION_NAME");
data = Arrays.asList("operation", "foo", "bar", "", "", "", "",
"", "", "tail");
formated = getFormattedValue(dataHeaders, data);
// ----format for 10----
Assert.assertEquals("tail----format for 10----", formated);
}
I have a simple SOQL query in java for extracting Salesforce standard object as follows -
String soqlQuery = "SELECT FirstName, LastName FROM Contact";
QueryResult qr = connection.query(soqlQuery);
I want to get the datatype of the object fields.
I have written a small function below which will provide the list of Phone fields and its label present in a Custom or Standard Object of your Salesforce ORG. I hope this might help you in writing the business logic for your code.
public list<String> getFieldsForSelectedObject(){
selectedPhoneNumber = ''; //to reset home number field
list<String> fieldsName = new list<String>();
selectedObject = 'Object Name' // This should have the object name for which we want to get the fields type
schemaMap = Schema.getGlobalDescribe(); //Populating the schema map
try{
if(selectedObject != null || selectedObject != '' || selectedObject != '--Select Object--'){
Map<String, Schema.SObjectField> fieldMap = schemaMap.get(selectedObject).getDescribe().fields.getMap();
for(Schema.SObjectField sfield : fieldMap.Values()){
schema.describefieldresult dfield = sfield.getDescribe();
schema.Displaytype disfield= dfield.getType();
system.debug('#######' + dfield );
if(dfield.getType() == Schema.displayType.Phone){// Over here I am trying to findout all the PHONE Type fields in the object(Both Custom/Standard)
fieldsName.add('Name:'+dfield.getName() +' Label:'+ dfield.getLabel ());
}
}
}
}catch(Exception ex){
apexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'There is no Phone or Fax Field Exist for selected Object!'));
}
return fieldsName;
}
Sample OUTPUT List of String::
Name: Home_Phone__c Label: Home Phone
Name: Office_Phone__c Label: Office Phone
Say that we have the below soql.
select FirstName,LastName from Contact limit 2
The query result in the QueryResult object looks like below.
{
[2]XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Bill, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Gates, children=[]}
]
},
XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Alan, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Donald, children=[]}
]
},
}
In order to parse the QueryResult and to take column names, I have implemented the below method that will return the column names in comma separated String. I have mentioned the logic inside the code.
public String getColumnNames(QueryResult soqlResponse)
{
String columns = ""
try
{
// We are looping inorder to pick the 1st record from the QueryResult
for (SObject record : soqlResponse.getRecords())
{
Iterator<XmlObject> xmlList = record.getChildren();
int counterXml = 0;
while(xmlList.hasNext())
{
XmlObject xObj = xmlList.next();
// Since the 1st 2 nodes contains metadata of some other information, we are starting from the 3rd node only
if(counterXml > 1)
{
columns += xObj.getName().getLocalPart() + ",";
}
counterXml++;
}
// Since we can get the column names from the 1st record, we are breaking the loop after the data of 1st record is read
break;
}
// We are removing the last comma in the string
columns = columns.substring(0, columns.length() - 1);
}
catch(Exception ex)
{
}
return columns;
}