I'm doing my homework on java I/O data, the problem is i'm not allowed to use object serialization to load data from a binary file.
Here is the assignment requirements:
Persistence, writing objects to file and reading objects from file (Text format)
• All objects should be written to a single file in a text format • All objects should be read form the same file • You should use JFileChooser
I have 3 classes: Unit
, Assessment
, and Task
.
The Assessment
class is abstract while IndividualAssessment
& GroupAssessment
are concrete subclasses.
Unit
has a collection of Assessment
and Assessment
has a collection of Task
s.
I could save all data to one text file with FileWriter
but I don't know how to read each line of the text file in to the proper Assessment
class.
What I mean is how do you recognize which line is for IndividualAssessment
or GroupAssessment
classes.
Here is the code I tried but it's not working:
BufferedReader bf = new BufferedReader(file);
While (bf.readLine != null){
Unit u = new Unit(bf);
diary.add(u);
try{
Assessment a = new IndividualAssessment(bf);
} catch (IOException ex){
Assessment a = new GroupAssessment(bf);
}
u.add(a);
Task t = new Task(bf);
a.add(t);
You should probably start with storing the content of the line you read in a String object. So it would look something like this:
String line = bf.readLine();
while(line != null) {
// Add code to look at your line to figure out what kind of object it
// represents. For example you could add a one character prefix to each
// line when you write it to specify which object it is ('U', 'A' or 'T').
// Based on that, you can call a constructor of the appropriate object that
// takes a String as input. Then let the constructor deal with parsing the
// line for the object it represents. This way you don't end up with some
// massive parsing routine.
char code = line.charAt(0);
if(code == 'T') {
Task task = new Task();
task.initFromString(line.sustring(1));
... Do something with your task
}
else if(code == ...) {
}
line = bf.readLine(); // Read the next line
}
Define some interface that all your object should implement:
public interface TextExportable {
public char getClassIdentifier();
public void initFromString(String s);
}
I'm not sure what your objects look like but let's say for example:
public class Task implements TextExportable {
private String name;
public Task() {} // For the pseudo-serialization
public Task(String name) { // For creating the object by hand
this.name = name;
}
public char getClassIdentifier() {
return 'T';
}
public String toString() {
return getClassIdentifier()+name;
}
public void initFromString(String line) {
this.name = line;
// Here, you would need extra parsing if you have more than one attribute
// and dissect the line
}
}
Having a BufferedReader
as a parameter on your constructors is quite an unconventional way of approaching this. BufferedReader
in your instance is a vehicle of delivery of your model data. And your model data should be about your use-case not about how you store it on disk, so it should be abstract enough. So instead, I would have a default constructor on each of your model classes, and then maybe a constructor that can be used to inialise all/some fields of the model classes.
One way to approach the file interface is to create file reader/writer utils that can load/save your data from/to a file. If the top of your class hierarchy is Unit
then I would have it like this:
Unit loadFromFile(String filename) {
// read unit, assessments etc in a format you choose, build the model and return it
...
}
void saveToFile(String filename) {
// write you model to a file
...
}
If I were you, I'd use JSON as my data format. It's readable and will be easy to debug.
I hope this helps.
Further to the accepted answer, I'd like to add that a clean way to do parsing is to follow the interpreter pattern
So Assuming a syntax with bnf like the following:
<Unit> ::= <Name>[<Assessments>]
<Assessments> ::= <Assessment> | <Assessment> <Assessments>
<Assessment> ::= <Name>:<Type>[<Tasks>]
<Tasks> ::= <Task> | <Task>,<Tasks>
<Name> ::= <String>
<Type> ::= Group | Ind
You might write something like this to parse it - I leave it as an exercise to write the Expression
classes used.
try {
BufferedReader br = new BufferedReader(new FileReader("File.txt"));
try {
String unit;
while ((unit = br.readLine()) != null) {
String unitName = unit.substring(0, unit.indexOf('['));
String assessments = unit.substring(unit.indexOf('[') + 1,
unit.lastIndexOf(']'));
List<AssessmentExpression> aes = new ArrayList<AssessmentExpression>();
while (!assessments.equals("")) {
String assessmentName = assessments.substring(0,
assessments.indexOf(':'));
String assessmentType = assessments.substring(
assessments.indexOf(':') + 1,
assessments.indexOf('['));
String tasks = assessments.substring(
assessments.indexOf('[') + 1,
assessments.indexOf(']'));
List<String> taskList = Arrays.asList(tasks.split(","));
aes.add(new AssessmentExpression(assessmentName,
assessmentType, taskList));
assessments = assessments.substring(assessments.indexOf(']')+1);
}
Unit u = new UnitExpression(unitName, aes).interpret();
}
} finally {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.