I'm using opencsv 3.6 in order to create a csv file starting from a java bean.
First of all, I tried this code:
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.bean.BeanToCsv;
import com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class CustomBean {
private String name;
private String surname;
public CustomBean(String n, String s) {
this.name = n;
this.surname = s;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getSurname() {
return surname;
}
public static void main(String[] args) {
Map<String,String> mapping = new HashMap<String,String>();
mapping.put("COLUMN1","name");
mapping.put("COLUMN2","surname");
HeaderColumnNameTranslateMappingStrategy<CustomBean> strategy = new HeaderColumnNameTranslateMappingStrategy<CustomBean>();
strategy.setType(CustomBean.class);
strategy.setColumnMapping(mapping);
ArrayList<CustomBean> customUsers = new ArrayList<CustomBean>();
customUsers.add(new CustomBean("Kobe","Bryant"));
BeanToCsv<CustomBean> bean = new BeanToCsv<CustomBean>();
try {
CSVWriter writer = new CSVWriter(new FileWriter("testOut.csv"));
bean.write(strategy, writer, customUsers);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But I had the following error:
Exception in thread "main" java.lang.RuntimeException: Error writing CSV !
at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:74)
at test.CustomBean.main(CustomBean.java:63)
Caused by: java.lang.NullPointerException
at com.opencsv.bean.HeaderColumnNameTranslateMappingStrategy.getColumnName(HeaderColumnNameTranslateMappingStrategy.java:45)
at com.opencsv.bean.HeaderColumnNameMappingStrategy.findDescriptor(HeaderColumnNameMappingStrategy.java:112)
at com.opencsv.bean.BeanToCsv.processHeader(BeanToCsv.java:103)
at com.opencsv.bean.BeanToCsv.write(BeanToCsv.java:69)
... 1 more
This happens because in opencsv source code in getColumnName method in the HeaderColumnNameTranslateMappingStrategy class there is the following line:
return col < header.length ? columnMapping.get(header[col].toUpperCase()) : null;
Therefore, header is null. This is true, in fact this class is a subclass of HeaderColumnNameMappingStrategy class that contains the header variable (String[] type) that is never initialized.
The only useful method I found in this class is captureHeader, but unfortunately it takes a CSVReader as input.
For this reason I created an empty csv file:
COLUMN1,COLUMN2
and I added the following lines at the beginning of the try/catch block:
CSVReader reader = new CSVReader(new FileReader("testIn.csv"));
strategy.captureHeader(reader);
In this way (that I really don't like because I have to create a csv file) I have no exception, but in the result csv file the name of the columns does not follow the mapping strategy:
"name","surname"
"Kobe","Bryant"
Two questions:
How can I have the expected result, i.e. the right column names in the csv file?
There is a way to not use the CSVReader class?
Looking at the source code of BeanToCsv, the processHeader(...) method does nothing with the supplied headers. Your only option is to create a custom strategy ( to avoid CSVReader ) and a custom BeanToCsv as below
public class CustomBean {
...
public static void main(String[] args) {
...
HeaderColumnNameTranslateMappingStrategy strategy = new CustomStrategy<CustomBean>();
strategy.setType(CustomBean.class);
strategy.setColumnMapping(mapping);
...
BeanToCsv bean = new CustomBeanToCsv<CustomBean>();
...
}
static class CustomStrategy<T> extends HeaderColumnNameTranslateMappingStrategy {
#Override
public void setColumnMapping(Map columnMapping) {
super.setColumnMapping(columnMapping);
header = new String[columnMapping.size()];
int i = 0;
for (Map.Entry entry : columnMapping.entrySet()) {
header[i] = entry.getKey().toUpperCase();
i++;
}
}
public String[] getHeader() {
return header;
}
}
static class CustomBeanToCsv<T> extends BeanToCsv {
#Override
protected String[] processHeader(MappingStrategy mapper) throws IntrospectionException {
if (mapper instanceof CustomStrategy) {
return ((CustomStrategy) mapper).getHeader();
} else {
return super.processHeader(mapper);
}
}
}
}
I have been using openCSV for five years now and I am still learning stuff about it. The issue is that the HeaderColumnNameMappingStrategy and HeaderColumnNameTranslateMappingStrategy were made for the CsvToBean. It wants a file to read to get the header out to see where the reader should read from.
For the BeanToCsv class use the ColumnPositionMappingStrategy class. You give it the class you are mapping and a list of the columns you want to map and it does the rest for you.
Here is a little test method I wrote that worked.
public void createUsingBeanToCsv(int numRecords, FileWriter fos) {
List<SmallRecord> smallRecords = new ArrayList<>(numRecords);
for (int i = 0; i < numRecords; i++) {
smallRecords.add(SmallRecordGenerator.createRecord(i));
}
BeanToCsv<SmallRecord> beanToCsv = new BeanToCsv<>();
ColumnPositionMappingStrategy<SmallRecord> strategy = new ColumnPositionMappingStrategy<>();
strategy.setType(SmallRecord.class);
String[] columns = new String[]{"bigDecimal_1", "name_1", "intNumber_1"};
strategy.setColumnMapping(columns);
beanToCsv.write(strategy, fos, smallRecords);
}
Related
Excel file I'm using
I want to parse a csv file and extract the name, like 'chair' and then a list of each possible colour, so ['Blue','Green','Yellow']. How can I do this?
I have created a class Object, that has a 'String name' and a 'Listcolours'.
CSVReader reader = new CSVReader(new FileReader(new File(url.toURI()).getAbsolutePath()));
Object<API> listings = new ArrayList<Object>();
String [] line;
List<String> colourList = new ArrayList<>();
reader.readNext();
while ((line = reader.readNext()) != null) {
String name = line[0];
String colour = line[1];
colourList.add(operation);
Object object = new Object(name,colourList);
listings.add(object);
}
You can create a Hashmap with key as name of the item and value as list of colors available.
I hope below snippet will solve your problem. Good Luck!!
HashMap<String,List<String>> data = new HashMap<>();
String [] line;
reader.readNext();
while ((line = reader.readNext()) != null) {
String name = line[0];
String colour = line[1];
if(data.containsKey(name.toLowerCase())){
data.get(name.toLowerCase()).add(colour.toLowerCase());
}
else{
List<String> colorList = new ArrayList<>();
colorList.add(colour.toLowerCase());
data.put(name.toLowerCase(),colorList);
}
}
#g00se what do you mean by a 'stream grouping operation'
I'll show you. Actually, in your case, since you're using a proper csv API, you can leverage that to create beans first, then do the grouping. Here's what I mean by stream grouping: we create a Map<String, HouseholdObject>> (yes that's my name for your entities - pity it's got the word 'object' in it, but never mind). That collects them into groups having the same name:
package com.technojeeves.opencsvbeans;
import com.opencsv.bean.CsvToBeanBuilder;
import java.util.List;
import java.util.Map;
import static java.util.stream.Collectors.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class HouseholdParser {
public static void main(String[] args) {
try {
List<HouseholdObject> objects = new HouseholdParser().read(new File(args[0]));
Map<String, List<HouseholdObject>> objectsGroupedByName =
objects.stream()
.skip(1)
.collect(groupingBy(HouseholdObject::getName));
System.out.println("The following groups of household objects were found:");
objectsGroupedByName.entrySet()
.stream()
.forEach(e -> {
System.out.println(e.getKey());
e.getValue()
.stream()
.forEach(v -> System.out.printf("\t%s%n", v.getColour()));
});
} catch (Throwable t) {
t.printStackTrace();
}
}
public List<HouseholdObject> read(File file) {
try (FileReader reader = new FileReader(file)) {
return new CsvToBeanBuilder(reader).withType(HouseholdObject.class).build().parse();
} catch (IOException e) {
throw new RuntimeException("Cannot read file: " + file.getName() + e);
}
}
}
Here's the bean I made for it:
package com.technojeeves.opencsvbeans;
import com.opencsv.bean.CsvBindByPosition;
import com.opencsv.bean.CsvCustomBindByPosition;
import com.opencsv.bean.AbstractBeanField;
import com.opencsv.bean.CsvRecurse;
public class HouseholdObject {
#CsvBindByPosition(position = 0)
private String name;
#CsvBindByPosition(position = 1)
private String colour;
public HouseholdObject() {
}
public HouseholdObject(String name, String colour) {
this.name = name;
this.colour = colour;
}
public String getName() {
return this.name;
}
public String getColour() {
return this.colour;
}
public void setName(String name) {
this.name = name;
}
public void setColour(String colour) {
this.colour = colour;
}
#Override
public String toString() {
return String.format("%s=%s,%s=%s", "name", name, "colour", colour);
}
#Override
public boolean equals(Object o) {
HouseholdObject other = (HouseholdObject)o;
return name.equals(other.name) && colour.equals(other.colour);
}
#Override
public int hashCode() {
return name.hashCode() * colour.hashCode();
}
}
And here's my output with your source CSV:
The following groups of household objects were found:
Table
Purple
Pink
Chair
Blue
Green
Yellow
Door
Yellow
I have a javafx program that brings up a filechooser to allow a user to pick and image an display it to a grid view with an inserted caption that pops up after an image was pciked. I save both the filepath and the caption to different arraylists [for now] and my goal is to save both to xml file so that I can unmarshall it when the application is re opened so the images would still be there. Right now I just want to be able to save the two strings to an xml file and then figure out the rest later. I am currently able to run my code with no errors until I reach my stop method where I try to save every image and caption the user has added to the array lists.
My JAXB Annotation:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ImageCap {
private String filePath;
private String caption;
public ImageCap() {
}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
public String getFilePath() {
return filePath;
}
#XmlElement
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getCaptions() {
return caption;
}
#XmlElement
public void setCaption(String caption) {
this.caption = caption;
}
}
And my main to test:
public static void main(String[] args) {launch(args);}
public void start(Stage primaryStage) throws JAXBException{
final JFXPanel bananarama = new JFXPanel();
//import the library (read))
// create the (initial) display
display.makeBrowseButton(primaryStage);
display.createDisplay(primaryStage);
// show user
primaryStage.show();
}#Override
public void stop() throws JAXBException{
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
//this.context = JAXBContext.newInstance(ImageCap.class);
//Marshaller marshaller = context.createMarshaller();
for(int i = 0; i < display.filePaths.size(); i++)
{
ImageCap imageCap = new ImageCap();
imageCap.setFilePath(display.filePaths.get(i));
imageCap.setCaption(display.captions.get(i).toString());
System.out.println(display.filePaths.get(i).toString());
try {
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(imageCap, file);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Here are my errors after the stop command:
this problem is related to the following location:
at public void ImageCap.setCaption(java.lang.String)
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.caption
at ImageCap
Class has two properties of the same name "filePath"
this problem is related to the following location:
at public java.lang.String ImageCap.getFilePath()
at ImageCap
this problem is related to the following location:
at private java.lang.String ImageCap.filePath
at ImageCap
but it specically cuts off at line 81 which is:
JAXBContext jaxbContext = JAXBContext.newInstance(ImageCap.class);
any ideas why?
If you have getter and setters for a field of the same name, then you need to use XmlAccessType.PROPERTY rather than XmlAccessType.FIELD:
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class ImageCap {
private String filePath;
private String caption;
...
Also, another problem that you will encounter is that you misspelled getCaptions() as plural, when it should be getCaption(). JAXB will complain about it as well.
Finally, by marshaling your file inside the loop, you are rewriting over and over the same file with the currently processed imageCap. If what you want is to marshall all imageCaps you need to put them on a List and marshall the List instead. In order to do that you'd need a new JAXB model class like:
#XmlRootElement(name = "myImageCapList")
class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
and you'd need to create an instance of this object wrapping your list of ImageCap objects (List<ImageCap>) and use it as the target to invoke the jaxbMarshaller.marshal method as shown in the following method:
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
also, you'll need to instantiate your JAXBContext appropriately:
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
The following is a complete working demo of this for you to play with:
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class JAXBMarshall {
private JAXBContext jaxbContext;
private Marshaller jaxbMarshaller;
public JAXBMarshall() throws JAXBException {
jaxbContext = JAXBContext.newInstance(ImageCapList.class);
jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
public void imageCapsMarshal(List<ImageCap> imageCaps, File outFile) {
try {
jaxbMarshaller.marshal(new ImageCapList(imageCaps), outFile);
} catch (JAXBException e) {
// HANDLE EXCEPTIONS
}
}
public static void main(String[] args) throws JAXBException {
JAXBMarshall jaxbMarshaller = new JAXBMarshall();
File file = new File("file.xml");
List<ImageCap> imageCaps = IntStream.range(0, 10)
.mapToObj(idx -> new ImageCap("my/file/path/" + idx, idx + ". The Caption!"))
.collect(Collectors.toList());
jaxbMarshaller.imageCapsMarshal(imageCaps, file);
}
#XmlRootElement(name = "myImageCapList")
static class ImageCapList {
#XmlElement
List<ImageCap> imageCap;
public ImageCapList() {}
public ImageCapList(List<ImageCap> imageCaps) {
this.imageCap = imageCaps;
}
}
#XmlRootElement
static class ImageCap {
#XmlElement
String filePath;
#XmlElement
String caption;
public ImageCap() {}
public ImageCap(String filePath, String caption) {
this.filePath = filePath;
this.caption = caption;
}
#Override
public String toString() {
return "ImageCap{" + "filePath=" + filePath + ", caption=" + caption + '}';
}
}
}
Complete code on GitHub
Hope this helps.
This is my first post so sorry if I mess something up or if I am not clear enough. I have been looking through online forums for several hours and spend more trying to figure it out for myself.
I am reading information from a file and I need a loop that creates an ArrayList every time it goes through.
static ArrayList<String> fileToArrayList(String infoFromFile)
{
ArrayList<String> smallerArray = new ArrayList<String>();
//This ArrayList needs to be different every time so that I can add them
//all to the same ArrayList
if (infoFromFile != null)
{
String[] splitData = infoFromFile.split(":");
for (int i = 0; i < splitData.length; i++)
{
if (!(splitData[i] == null) || !(splitData[i].length() == 0))
{
smallerArray.add(splitData[i].trim());
}
}
}
The reason I need to do this is that I am creating an app for a school project that reads questions from a delimited text file. I have a loop earlier that reads one line at a time from the text. I will insert that string into this program.
How do I make the ArrayList smallerArray a separate ArrayList everytime it goes through this method?
I need this so I can have an ArrayList of each of these ArrayList
Here is a sample code of what you intend to do -
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class SimpleFileReader {
private static final String DELEMETER = ":";
private String filename = null;
public SimpleFileReader() {
super();
}
public SimpleFileReader(String filename) {
super();
setFilename(filename);
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public List<List<String>> getRowSet() throws IOException {
List<List<String>> rows = new ArrayList<>();
try (Stream<String> stream = Files.lines(Paths.get(filename))) {
stream.forEach(row -> rows.add(Arrays.asList(row.split(DELEMETER))));
}
return rows;
}
}
And, here is the JUnit test for the above code -
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Test;
public class SimpleFileReaderTest {
public SimpleFileReaderTest() {
super();
}
#Test
public void testFileReader() {
try {
SimpleFileReader reader = new SimpleFileReader("c:/temp/sample-input.txt");
List<List<String>> rows = reader.getRowSet();
int expectedValue = 3; // number of actual lines in the sample file
int actualValue = rows.size(); // number of rows in the list
if (actualValue != expectedValue) {
fail(String.format("Expected value for the row count is %d, whereas obtained value is %d", expectedValue, actualValue));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
How would I go about saving a String Vector to a file every time it is edited?
So let's say I have usernames in a vector, after I add or delete a username I'd like it to save that vector so if the program is closed, it will show the most recent elements.
This should help you get started.
As JB Nizet said, you should use an ArrayList.
I also went ahead and used Java 7 autocloseable functionality, which ensures you close file handles appropriately.
Of course, you will need to validate your input, and you will want to take care about what you persist. I suspect that you will soon want to consider a better storage strategy, however, this will get you started.
In addition, since this is acting like a collection, you should add hashcode and equals. For brevity sake, I did not add those.
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
public class PersistedCollection {
private static final String NEWLINE_SEPARATOR = System.getProperty("line.separator");
private final List<String> values;
private final File file;
public PersistedCollection(File file) {
this.values = new ArrayList<>();
this.file = file;
}
public void add(String value) {
// You should validate this value. Remove carriage returns, make sure it meets your value specifications.
values.add(value);
persist();
}
public void remove(String value) {
values.remove(value);
persist();
}
private void persist() {
// Using Java 7 autocloseable to ensure that the output stream is closed, even in exceptional circumstances.
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(this.file), 8192); Writer writer = new PrintWriter(outputStream)) {
for (String value : values) {
writer.append(value);
writer.append(NEWLINE_SEPARATOR);
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PersistedCollection [values=");
builder.append(values);
builder.append(", file=");
builder.append(file);
builder.append("]");
return builder.toString();
}
public static void main(String[] arguments) {
PersistedCollection persistedCollection = new PersistedCollection(new File("/tmp/test.txt"));
persistedCollection.add("jazeee");
persistedCollection.add("temporary user");
persistedCollection.add("user402442");
persistedCollection.add("JB Nizet");
persistedCollection.remove("temporary user");
System.out.println(persistedCollection);
}
}
Another solution would be to create a class where you add all the methods required to read from a file of usernames (one username per line). Then you can refer to this class from anywhere (as the modifier is public) and call the methods such that you will add or remove usernames from that file.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.io.File;
public class Test {
private static BufferedWriter bw;
private static ArrayList<String> vector=new ArrayList<String>();
private static String everything;
//add an username
public static void add(String x){
vector.add(x);
}
//remove an username
public static void remove(String x){
vector.remove(x);
}
//update the file with the new vector of usernames
public static void updateToFile() throws IOException{
File username = new File("/home/path/to/the/file");
FileWriter fw = new FileWriter(username.getAbsoluteFile());
bw= new BufferedWriter(fw);
for (String x:vector){
bw.write(x.toString());
bw.write("\n");
}
bw.close();
}
//you call this method to initialise your vector of usernames
//this implies that you already have a file of usernames
//one username per line
public static void setUsername() throws IOException{
vector=new ArrayList<String>();
BufferedReader br = new BufferedReader(new FileReader("/home/path/to/the/file"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
}
everything = sb.toString();
} finally {
br.close();
}
String lines[] = everything.split("\\r?\\n");
for (String x:lines){
vector.add(x);
}
}
//print your usernames in the console
public static void printUsers(){
for (String User:vector){
System.out.println(User);
}
}
}
Then it gets as easy as this:
import java.io.IOException;
public class MainTest {
public static void main(String[] args) throws IOException{
Test.setUsername();
Test.printUsers();
Test.add("username5");
Test.remove("username2");
System.out.println("// add username5; remove username2");
Test.printUsers();
System.out.println("// file has been updated with the new state");
Test.updateToFile();
System.out.println("// veryfing update");
Test.setUsername();
Test.printUsers();
}
}
The output:
(this first 4 users is what I have in the file)
username1
username2
username3
username4
// add username5; remove username2
username1
username3
username4
username5
// file has been updated with the new state
// verifying update
username1
username3
username4
username5
I am working on a little todolist-program and i'm getting a weird bug that i never had before. I have 4 classes: 1 POJO class that contains the todo-data:
public class Todo implements Comparable {
private String title;
private String task;
private boolean done;
public Todo(String title, String task) {
this.title = title;
this.task = task;
}
public String getTitle() {
return title;
}
public void setTitle(String newTitle) {
title = newTitle;
}
public String getTask() {
return task;
}
public void setTask(String newTask) {
task = newTask;
}
public boolean isDone() {
return done;
}
public void setDone(boolean isDone) {
done = isDone;
}
public int compareTo(Object obj) {
Todo todo = (Todo) obj;
return getTitle().compareTo(todo.getTitle());
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Todo {\n");
sb.append("Title: \"");
sb.append(getTitle() + "\";\n");
sb.append("Task: \"");
sb.append(getTask() + "\";\n");
sb.append("}");
return sb.toString();
}
}
Then I have a class that stores and loads my todos:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ListStorage {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
FileWriter writer;
BufferedReader reader;
public void storeList(List list, String filename) throws IOException {
String json = gson.toJson(list);
writer = new FileWriter(filename);
writer.write(json);
writer.close();
}
public List loadList(String filename) throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
List list = gson.fromJson(reader, List.class);
return list;
}
}
Then I have a 'Manager' class that is basically my controller:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Manager {
private List<Todo> todos = new ArrayList<>();
private ListStorage storage = new ListStorage();
public List getTodos() {
return todos;
}
public void setTodos(List newTodos) {
todos = newTodos;
}
public ListStorage getStorage() {
return storage;
}
public void add(String title, String task) {
todos.add(new Todo(title, task));
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
public void remove(int index) {
todos.remove(index);
sort();
try {
storage.storeList(todos, "todos");
} catch(Exception e) {
e.printStackTrace();
}
}
private void sort() {
Collections.sort(todos);
}
}
And finally there is my main-class for testing my code (The bug seems to be here):
class CLITodo {
public static void main(String[] args) {
Manager man = new Manager();
man.add("Hello", "Bye");
man.add("Foo", "Bar");
try {
man.setTodos(man.getStorage().loadList("todos"));
} catch(Exception e) {
}
java.util.List<Todo> todos = man.getTodos();
for (Todo t : todos) {
System.out.println(t);
}
}
}
The error message I get when I leave the <Todo> in CLITodo class is:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to Todo at CLITodo.main(CLITodo.java:13)
When I remove <Todo> in CLITodo I get this error:
CLITodo.java:13:19: error: incompatible types
for (Todo t : todos) {
^
required: Todo
found: Object
Why does this error occur? My Manager classes getTodos()-Method returns a List of type Todo yet the compiler tells me that it is just an Object (which it is of course but it is a collection as well, which should actually work).
This is the first time this error occured and I really can't seem to find what is causing it.
When you don't specify what nested type to use to deserialize your JSON, like you do here
List list = gson.fromJson(reader, List.class); // All it knows is that the root json is a List
Gson uses LinkedTreeMap.
What you really want is
List list = gson.fromJson(reader, new TypeToken<List<Todo>>(){}.getType());