2

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 Tasks.

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);
3
  • "not working:" What happened? How was that different to what you expected? Commented May 25, 2012 at 16:53
  • "I'm not allowed to use object serialization" Any way you look at it, the task is to write (serialize) & read (deserialize) the object. DYM you are not allowed to use code that implements Serializable? What about using XMLEncoder/XMLDecoder? Commented May 25, 2012 at 17:01
  • nothing but PrintWriter to write and BufferedReader to read from one single text file. I used object serialization and the teacher forced me to do again unless I want zero mark. Commented May 25, 2012 at 17:08

3 Answers 3

1

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
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Care to enlighten me, what does the prefix makes difference? could you give me more example in code? Because when I save all data to a text file, I save it in sequential order using enhanced for loop, so I'm thinking a way to read it back, but group and individual ass give me hard time when creating new object from each line of text file
I've edited the response. Because this is homework, I can't ethically give you the whole solution, but this should get you headed in the right direction.
Tks so much, I could handle it from now :)
"I can't ethically give you the whole solution" In addition to ethical reasons, the student learns more this way. :)
0

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.

5 Comments

Thanks, I also have the method like yours in each class, the tricky is i'm now allowed to used any thing but Printwriter and BufferedReader to manipulate persistent data :(
Well my point is - you don't need your classes to care about reading and writing. If you stored your whole domain model in a JSON where it is structured the way you want it to be, you can save it and load it using BufferedReader. And then use JSON library to parse JSON to create your model in Java.
I can see your point but the teacher said no JSON, no XML, no Serialization. I still don't figure out how he could handle it. Anyway, I might give it up :D
If JSON is prohibited (I can't see why - it's only a structured text format) you can use what @mprivat has suggested above: store your models as lines, with prefixes to help you understand which line is which class.
In each class, I have a constructor that use bufferedreader as a parameter, in constructor, I use line = bf.readLine().split(","); to assign class attribute to line array, it worked if I do it manually, but It caused exception error if read a file in for loop. The problem is individual assessment different with group assessment. Is there any way to check if a string match prefix word in line, so I could create new object based on that??? or could you give me more example based on @mprivat hint?
0

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();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.