Reputation:
I made a small java swing application to store list of students in a file students.txt
.
But I am facing a problem while saving many students into the file:
when I click the "save" button, only one student record is stored. If I input new data in the text fields and click save button again it saves only newly created student, overwriting older records, while I want a new student to be appended to the existing student list in a file.
Here is my code :
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class studentTest extends JFrame {
JPanel p1, p2, p3, p4, p5, p6;
JTextField f1, f2, f3, f4;
JLabel l1, l2, l3, l4;
JButton b1, b2, b3;
public studentTest() {
setBounds(100, 150, 300, 400);
b1 = new JButton("Save");
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<Student> Students = new ArrayList();
Student newStudents = new Student();
newStudents.setStudentFName(f1.getText());
newStudents.setStudentLName(f2.getText());
newStudents.setGPA(f4.getText());
newStudents.setID(f3.getText());
Students.add(newStudents);
try{
PrintWriter pw = new PrintWriter(new FileOutputStream("/Users/Yukki/Desktop/Students.txt"));
pw.println(newStudents+"\n");
pw.close();
} catch (FileNotFoundException ex) {
}
}
});
b2 = new JButton("First Record");
b3 = new JButton("Next");
l1 = new JLabel(" FirstName :");
l2 = new JLabel(" LastName :");
l3 = new JLabel(" ID :");
l4 = new JLabel(" GPA :");
f1 = new JTextField(10);
f2 = new JTextField(10);
f3 = new JTextField(10);
f4 = new JTextField(10);
p1 = new JPanel(new GridLayout(1, 2));
p1.add(l1);
p1.add(f1);
p2 = new JPanel(new GridLayout(1, 2));
p2.add(l2);
p2.add(f2);
p3 = new JPanel(new GridLayout(1, 2));
p3.add(l3);
p3.add(f3);
p4 = new JPanel(new GridLayout(1, 2));
p4.add(l4);
p4.add(f4);
p5 = new JPanel(new GridLayout(1, 1));
p5.add(b1);
p6 = new JPanel(new GridLayout(1, 2));
p6.add(b2);
p6.add(b3);
setLayout(new GridLayout(6, 1));
add(p1);
add(p2);
add(p3);
add(p4);
add(p5);
add(p6);
}
public static void main(String[] args) {
(new studentTest()).show();
}
}
Upvotes: 2
Views: 2547
Reputation: 347194
While there are a number of of solutions you could try, I would recommend. Trying to use Java Architecture for XML Binding (JAXB), as it will allow you to define the means by which a instance of Student can be out putted to XML.
From there, it should allow you to add multiple instances to the same file.
It might be suggested that you serialise the objects, but personally, I'd avoid it. It was only intended to be used for the short term storage of an object, typically for transmission over the wire, but that's just my opinion...
Update with example...
This is based on the examples from JAXB exmaple: Marshalling and Unmarshalling list or set of objects and JAXB Hello World Example
First, we need to define the Student
class and JAXB annotations. This allows us to describe how instances of the object should be marshalled (translated)
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Student {
String name;
int age;
int id;
// This is a requirement of JAXB...
public Student() {
}
public Student(int id, String name, int age) {
this.name = name;
this.age = age;
this.id = id;
}
public String getName() {
return name;
}
@XmlElement
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
@XmlElement
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
@XmlAttribute
public void setId(int id) {
this.id = id;
}
}
Next, we need to create a simple wrapper class that can contain a list of objects. This is required so that the JAXB API can gain access to the internal data more easily...
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType (XmlAccessType.FIELD)
@XmlRootElement
public class Students implements Iterable<Student> {
private List<Student> students;
public Students() {
this.students = new ArrayList<>(25);
}
public Student get(int index) {
return students.get(index);
}
public void add(Student student) {
students.add(student);
}
public void remove(Student student) {
students.remove(student);
}
public int size() {
return students.size();
}
@Override
public String toString() {
return "List-o-Students: " + size();
}
@Override
public Iterator<Student> iterator() {
return students.iterator();
}
}
Then, finally, we need to test it...
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.jaxb.test.Student;
import org.jaxb.test.Students;
public class JAXBExample {
public static void main(String[] args) {
Students students = new Students();
students.add(new Student(100, "Bob", 29));
students.add(new Student(101, "John", 19));
students.add(new Student(102, "Joe", 39));
students.add(new Student(103, "Jane", 25));
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Students.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(students, file);
jaxbMarshaller.marshal(students, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Students.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
students = (Students) jaxbUnmarshaller.unmarshal(file);
System.out.println(students);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Which outputs...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<students>
<students id="100">
<age>29</age>
<name>Bob</name>
</students>
<students id="101">
<age>19</age>
<name>John</name>
</students>
<students id="102">
<age>39</age>
<name>Joe</name>
</students>
<students id="103">
<age>25</age>
<name>Jane</name>
</students>
</students>
List-o-Students: 4
Updated with updatable example
The following allows you to dynamically update the XML overtime. It pretty much the same concept as read, write, read, but provides some additional functionality...
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("file.xml");
Document document = db.parse(xml);
// Create a new context...
JAXBContext jc = JAXBContext.newInstance(Students.class);
// Create a new Binder from the context
Binder<Node> binder = jc.createBinder();
// Load the document
Students students = (Students) binder.unmarshal(document);
// Update the student at position 0
Student student = students.get(0);
student.setAge(100);
// Add a new student
students.add(new Student(200, "Harry", 65));
// Update the document
binder.updateXML(students);
// Write the contents back to the file..
FileOutputStream fos = null;
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
fos = new FileOutputStream(xml);
t.transform(new DOMSource(document), new StreamResult(fos));
} catch (TransformerFactoryConfigurationError | TransformerException exp) {
exp.printStackTrace();
} finally {
try {
fos.close();
} catch (Exception e) {
}
}
} catch (ParserConfigurationException | SAXException | IOException | JAXBException exp) {
exp.printStackTrace();
}
This might look a little complex, but it means you can now dynamically modify the underlying document.
For your needs though, I would simply read the existing list in at the start, update the list and finally, when you're ready, write it back...but that's just me...
And finally, a fully runnable example...
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jaxb.test.Student;
import org.jaxb.test.Students;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class JAXBExample {
public static void main(String[] args) {
Students students = new Students();
students.add(new Student(100, "Bob", 29));
students.add(new Student(101, "John", 19));
students.add(new Student(102, "Joe", 39));
students.add(new Student(103, "Jane", 25));
write(students);
students = null;
students = read();
for (Student student : students) {
System.out.println(student);
}
update();
students = null;
students = read();
for (Student student : students) {
System.out.println(student);
}
}
public static void write(Students students) {
System.out.println("----> Write");
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Students.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(students, file);
// jaxbMarshaller.marshal(students, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
public static Students read() {
System.out.println("----< Read");
Students students = null;
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Students.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
students = (Students) jaxbUnmarshaller.unmarshal(file);
System.out.println(students);
} catch (JAXBException e) {
e.printStackTrace();
}
return students;
}
public static void update() {
System.out.println("----> Update");
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("file.xml");
Document document = db.parse(xml);
JAXBContext jc = JAXBContext.newInstance(Students.class);
Binder<Node> binder = jc.createBinder();
Students students = (Students) binder.unmarshal(document);
Student student = students.get(0);
student.setAge(100);
students.add(new Student(200, "Harry", 65));
binder.updateXML(students);
FileOutputStream fos = null;
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
fos = new FileOutputStream(xml);
t.transform(new DOMSource(document), new StreamResult(fos));
} catch (TransformerFactoryConfigurationError | TransformerException exp) {
exp.printStackTrace();
} finally {
try {
fos.close();
} catch (Exception e) {
}
}
} catch (ParserConfigurationException | SAXException | IOException | JAXBException exp) {
exp.printStackTrace();
}
}
}
Upvotes: 1
Reputation: 5213
"newStudents" is just an Student instance. Iterate the list and write each item. (list name should begin with a simple letter btw). Also you are adding just one Student to the list.
for (Student student: students) {
pw.println(student);
}
You can override toString() of student to get a meaningful string representation.
Upvotes: 1