How to count number of objects stored in a *.ser file - java

I'm trying to read all the objects stored in a *.ser file and store them in a array of objects. How can I get the number of objects stored in that file(So that I can declare the array to be number_of_objects long)?
I've checked the API and was unable to find a Desirable function.
-edit-
A Part of the code:
Ser[] objTest2 = new Ser[number_of_objects];
for(int i=0; i<=number_of_objects, i++) {
objTest2[i] = (Ser)testOS2.readObject();
objTest2[i].printIt();
}

What you want to look at is the ArrayList class.
It is basically a dynamically growing Array.
You can add items to it like so:
ArrayList list = new ArrayList();
list.add(someObject);
list.add(anotherBoject);
The list will grow as you add new items to it. So you don't have to know the size ahead of time.
If you need to get an array out if the List at the end you can use the toArray() method of List.
Object[] arr = list.toArray(new Object[list.size()]);
Edit:
Here is a general implementation of what you need:
List<Ser> objTest2 = new ArrayList<Ser>();
while (testOS2.available > 0) {
Ser toAdd = ((Ser)testOS2.readObject());
toAdd.printIt();
objTest2.add(toAdd);
}
*I don't think available() is a reliable test for whether or not there are more bytes to read.

Year's later this post is still relevant. I was looking for a way to loop through a .ser file while de-serializing each file, and to some extent, Rohit Singh's post helped. This is my version of the same though:
ArrayList<Profile> availableProfiles = new ArrayList<Profile>();
try {
FileInputStream fileStream = new FileInputStream("profiles.ser");
ObjectInputStream os = new ObjectInputStream(fileStream);
Object profileObject = null;
while((profileObject = os.readObject()) != null) {
Profile castObject = (Profile) profileObject;
availableProfiles.add(castObject);
}
os.close();
} catch(Exception ex) {
if(ex instanceof EOFException) {
out.println("End of file reached!");
out.println("Total profiles found is: " + availableProfiles.size());
} else if(ex instanceof FileNotFoundException) {
out.println("File not found! \n Answer the following to create your profile");
createProfile();
}
}
The most important part is the position of the while-loop. In my version, that loop does not create a new FileInputStream or ObjectInputStream like Singh's does. That will make the ObjectInputStream read the .ser file afresh each time those two are created, and as a result, you only add() one Profile object to the ArrayList- the first one to be serialized- each time the loop restarts.
Instead, we only loop the with the readObject() method until it produces a null signifying no other object was found in the file, and it triggers the EOFException.

while(true)
{
try
{
Employee e=(Employee) ois.readObject();
System.out.println("successfully deserialized.........showing details of object.");
e.display();
}
catch(Exception e)
{
if(e instanceof java.io.EOFException)
{
System.out.println("All objects read and displayed");
break;
}
else
{
System.out.println("Some Exception Occured.");
e.printStackTrace();
}
}
}

Just keep reading objects until you get EOFException. That's what it's for. And use a List instead of an array so you don't need the count in advance.

Related

Objects not read completely when reading from a File in Java

I'm saving objects from two array lists to a file and if I restart the application I have called a read method at the startup which will read the data from the file and add them to the array lists
But when reading from the file only the first object is been read and added to the list even when I add several objects to the array list and save to the file when reading only the first object is read
My Method to Read Objects from the File:
void readData() throws IOException{
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("systemData.txt"))) {
doctorList.add((Doctor) in.readObject());
consultations.add((Consultation) in.readObject());
} catch (EOFException ignored){
} catch (IOException | ClassNotFoundException e ) {
e.printStackTrace();
}catch (ClassCastException ignored) {
}
}
My Method to Save Objects to the File:
#Override
public void saveFile() {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("systemData.txt"))) {
for (Doctor doctor : doctorList) {
out.writeObject(doctor);
System.out.println("Doctor data saved to the file");
}
for (Consultation consultation: consultations){
out.writeObject(consultation);
System.out.println("Consultation data saved to the file");
}
} catch (IOException e) {
e.printStackTrace();
}
}
As #Slaw hints at you need to inform the read method how many many Doctors to read, then the number of Consulations. In short readObject() does not read the collection, only a single item... that's because you wrote item by item.
Think of it like this:
Imagining stuffing 4 apples and 6 oranges into a legwarmer.
When you write/put in, you call writeObject() 4+6 times. When you read you are pulling things out of the leg warmer from the other end (that's why I chose a leg warmer and not a sock!) and you they come out in exactly the order you put them in. But just like a leg warmer/sock you cannot immediately tell what is going to come out as the objects have similar size...
Certainly your code is quite wrote since you invoke readObject() just twice. So you read just one of the Doctors into the doctors collection then what happens? Well, the second readObject() reads a Doctor but your code tries to cast it into a Consultation which will raise a ClassCastException. Your code catches this but then swallows the Exception so you don't know that that happened! (Hence why it is always bad to have empty catch blocks.
With this low level approach, people develop their own protocols to define how to know what is coming out of the leg-warmer, like #Slaw suggests,. In rough code (I didn't check this compiled) it would be:
out.writeObject(doctorList.size());
for (Doctor doctor : doctorList) {
out.writeObject(doctor);
}
out.writeObject(consultations.size());
for (Consultation consultation: consultations){
out.writeObject(consultation);
}
out.flush();
then read
Integer expectedDoctors = (Integer) in.readObject();
for(int i=0; i<expectedDoctors; i++) {
doctorList.add((Doctor) in.readObject());
}
Integer expected Consultations = (Integer) in.readObject();
for(int i=0; i<Consultations; i++) {
consultations.add((Consultation) in.readObject());
}
A simpler approach would be not to write Doctor by Doctor but the whole collection in one go:
out.writeObject(doctorList);
out.writeObject(consultations;
out.flush();
then read
doctorList.addAll((List) in.readObject());
consultations.addAll((List) in.readObject());
Now you don't need the "here comes" counter. Note addAll() and the changed cast.
The other things you must do is call out.flush(); to ensure all the data has been written - essential if you were to wrap the output stream with a Buffer.

Deserializing returns null object Java

Currently writing an application like Booking and I am in the stage where i want to store my infomation in files. I have created a Serializable Database class which has a field with a pathname and 2 methods for read/write . I have about 8 other classes which extend the Database and each holds a Hashmap and some querying methods. Naturally I read the Databases from my files before i start the application and i write before exiting, but I ran into a problem where the objects I am reading are all null. I've been at it for 2 hours now and I need a second opinion. This is the database's read/write methods:
public void write() {
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileOutputStream fileOut = new FileOutputStream(this.filename);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(this);
objectOut.close();
System.out.println("The Object was successfully written to a file");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public Object read() {
Object obj = null;
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileInputStream fileIn = new FileInputStream(this.filename);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
obj = objectIn.readObject();
System.out.println("The Object was successfully read from the file");
objectIn.close();
} catch (EOFException ex) {
return obj;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
And here is the way I load them (Which is probably the problem) in my Application class
private void loadData() {
accommodationReviewsDatabase = (AccommodationReviews) accommodationReviewsDatabase.read();
brokerAccommodationsDatabase = (BrokerAccommodations) brokerAccommodationsDatabase.read();
credentialsUserDatabase = (CredentialsUser) credentialsUserDatabase.read();
customerReviewsDatabase = (CustomerReviews) customerReviewsDatabase.read();
userConfirmationsDatabase = (UserConfirmations) userConfirmationsDatabase.read();
userMessagesDatabase = (UserMessages) userMessagesDatabase.read();
}
private void writeData() {
accommodationReviewsDatabase.write();
brokerAccommodationsDatabase.write();
credentialsUserDatabase.write();
customerReviewsDatabase.write();
userConfirmationsDatabase.write();
userMessagesDatabase.write();
}
Some extra information that may be asked :
All my classes that I am storing are serializable
The files I am storing the databases are all *.ser (Thats the extension I found)
The files are stored inside the project
If your read() method completes without an EOFException, it ends with return null;. You should return obj;, the object you read.
You should not expect that EOFException will be thrown if your read succeeds. The EOFException would indicate that it ran out of data while it was trying to read your object, and could not complete successfully.
If you do get an EOFException, it is probably a good idea to give some indication instead of silently returning. Silent catch blocks deny you information that could be useful for debugging.

Save an arraylist to cache file and retrieve it

I want to create two methods. The one should store and arraylist to the cache and the other should retrieve the arraylist from the cache. The code that I've tried is the following:
public class ByteConverter<T> {
public byte[] write_to_byte_array(ArrayList<T> list,File file){
// write to byte array
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e1) {
Log.d("file not found", "file not found", e1);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
for (T element : list) {
try {
out.writeUTF((String) element);
} catch (IOException e) {
Log.d("Error converting List to byte array", "Error converting List to byte array", e);
}
}
byte[] bytes = baos.toByteArray();
int len1 = 0;
while (len1 < bytes.length) {
try {
fos.write(bytes, 0, len1);
len1++;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
public String read_to_byte_array(byte[] bytes) throws IOException{
// read from byte array
String element = null;
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
DataInputStream in = new DataInputStream(bais);
while (in.available() > 0) {
element = in.readUTF();
}
return element;
}
}
However the aforementioned methods doesn't write anything to the file. I don't know why. Can anyone help me? Also I'm taking a ClassCastException at this line:
out.writeUTF((String) element);
First, the ClassCastExcetion is the reason you are not getting any output. The exception is thrown before your code writes to the file ...
Second, the ClassCastException is happening because the element is not a String. In theory, you could call element.toString() to get a string representation, but the chances are that that representation will NOT be suitable for your purpose.
So what is the correct approach?
Well it depends on the types of the objects in list.
If they implement Serializable (or Externalizable) then you should be able to use Java serialization. Read this section of the Java tutorial for more details.
Another option might be to use something like JAXP or GSON which can serialize POJOs to XML and JSON respectively.
The final fallback is to code the serialization method (and a corresponding serialization method) by hand.
Finally, I should point out some other errors in your code:
1) This code is wrong:
int len1 = 0;
while (len1 < bytes.length) {
fos.write(bytes, 0, len1);
len1++;
}
This will write:
bytes[0]
bytes[0], bytes[1]
bytes[0], bytes[1], bytes[2]
and so on.
The correct way to write bytes is:
fos.write(bytes);
or
fos.write(bytes, 0 bytes.length);
2) Your exception handling logic is incorrect. In 3 or 4 places you catch an exception, print or log something ... and then continue as if nothing had gone wrong. What you should probably do is let the exceptions propagate, and handle them at a higher level.
3) If something goes wrong in the write_to_byte_array method it is likely to leak a file descriptor. Best practice is to either close resources in a finally block, or use the new Java 7 try-with-resource syntax.
4) The names of the read_to_byte_array and write_to_byte_array methods violate the accepted Java style guidelines. They should be readToByteArray (or better still readFromByteArray) and writeToByteArray.
Rergarding ClassCastException, if element of type T isn't a String you will get that exception since that object cannot be casted to String. Maybe you are looking for toString method of element.
Also take a look to the FileUtils class of Apache Commons and the method writeLines
use Serializable Object , because ArrayList implement Serializable , must be implement Serializable.
youself can custom data type ,for example:
length|value|length|value....
4bytes|bytes|4bytes|bytes....

Reading from a binary file object

I have to read a series of object from a binary file.
I use:
ObjectOutputStream obj = new ObjectOutputStream(new FileInputStream(fame));
obj.readObject(p);
where p is a reference to an object I had created. How can I read the entire file until the end?
I can use:
while(p!=null){}
?
readObject() returns null if and only if you wrote a null. The correct technique is to catch EOFException and when you get it close the stream and exit the reading loop.
Let's assume you meant ObjectInputStream and p = obj.readObject().
I would do something like this: (this is wrong, see EDIT below)
FileInputStream fstream = new FileInputStream(fileName);
try {
ObjectInputStream ostream = new ObjectInputStream(fstream);
while (ostream.available() > 0) {
Object obj = ostream.readObject();
// do something with obj
}
} finally {
fstream.close();
}
EDIT
I take it back! EJP rightly points out that the use of available() is incorrect here. I think the fixed code might be:
FileInputStream fstream = new FileInputStream(fileName);
try {
ObjectInputStream ostream = new ObjectInputStream(fstream);
while (true) {
Object obj;
try {
obj = ostream.readObject();
} catch (EOFException e) {
break;
}
// do something with obj
}
} finally {
fstream.close();
}
Although the documentation for readObject() doesn't explicitly say that EOFException is thrown at the end of the stream, it seems to be implied and may be the only way to detect the end of the stream.
Another option if you control the code that wrote the stream would be to write an object count at the beginning, or a flag after each object indicating whether the previous object was the final one.
If you want to read object into your program, then you have to use ObjectInputStream, not ObjectOutputStream.
And if you will store a bunch of objects, then use an appropriate Collection for writing to file and reading from it. The API documentation for readObject does not state that it will return null or throw an exception if EOF is reached. So to be on the safe side, use Collections.
You may also want to read API docs on ObjectInputStream and ObjectOutputStream.
Boolean i = true;
while(i) {
try {
System.out.println(reader.readObject());
} catch(Exception e) {
i = false;
System.out.println("Dead end");
}
}
Guava's Files.toByteArray does what you want so if fame in your code is a File, then
import com.google.common.io.Files;
...
byte[] fameBytes = Files.toByteArray(fame);

How do I update a File created by openFileOutput

I'm currently building an application where the user will generate data over time and, should he/she has an internet connection, transmit it to the web. However, if he doesn't have web access, I need to store this data in the phone until the user recovers his access, when I'll need to recover this data to be transmitted. However, I'm facing lots of troubles to do this, as per below.
Note: before anything, I'm using a local java-created file because I know no other way to save/restore this data on the device. If you happen to know any other way to store/access this data from within the device please feel free to comment here.
Just for reference,
phantoms is an ArrayList containing objects with the data I need to
store,
Arquivador is the class that I'm using to make my data persistent and to recover it,
Funcionario is the class with the data generated by the program (just a few strings and numbers)
I am able to write a file to the file system through the code below, on my Activity:
try {
arq = new Arquivador();
arq.addFirstObjectInFile(
openFileOutput("dados.jlog", MODE_WORLD_WRITEABLE),
phantoms.get(0));
phantoms.remove(phantoms.get(0));
for (Funcionario func : phantoms) {
arq.addObjectInFile(openFileOutput("dados.jlog", MODE_APPEND),
func);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
}
Here is the code inside Arquivador that adds the data to a file:
public void addObjectInFile(FileOutputStream arquivo,
Object objetoAAdicionar) {
try {
ObjectOutputStream aoos = new ObjectOutputStream(arquivo);
aoos.writeObject(objetoAAdicionar);
aoos.close();
} catch (IOException ioe) {
Log.d(TAG_NAME, "Erro no Appendable OOS.");
}
}
public void addFirstObjectInFile(FileOutputStream arquivo,
Object objetoAAdicionar) {
try {
AppendableObjectOutputStream aoos = new AppendableObjectOutputStream(
arquivo);
aoos.writeObject(objetoAAdicionar);
aoos.close();
} catch (IOException ioe) {
Log.d(TAG_NAME, "Erro no Appendable OOS.");
}
}
You will notice that I'm adding data to persistence in 2 steps, the first Object and the rest of them. This was an idea I saw on this post, here in StackOverflow, to allow appending data to a Java generated file. I have no problem with this code, it works perfectly.
Later on, back on my Activity, the internet connection is detected and I try to recover the file saved on the disk:
phantoms = new ArrayList<Funcionario>();
Object obj = arq.readObjectFromFile(openFileInput("dados.jlog"));
Funcionario func = null;
if (obj instanceof Funcionario) {
func = (Funcionario) obj;
}
while (func != null) {
phantoms.add(func);
arq.removeObjectFromFile(openFileInput("dados.jlog"), func,
getApplicationContext());
func = (Funcionario) arq
.readObjectFromFile(openFileInput("dados.jlog"));
}
The original idea was to read 1 object at a time, then attempt to transmit it and, if successful, erase the object from the file (so it didn't get retransmitted). However, I was having too many error messages with this. Instead, I decided to load all the objects at once, one by one, to see where my problem was more clearly.
Back to the Arquivador class:
public Object readObjectFromFile(FileInputStream arquivo) {
Object retorno = null;
if (arquivo.equals(null)) {
Log.e(TAG_NAME, "FIS is null!");
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(arquivo);
retorno = ois.readObject();
} catch (IOException ioex) {
} catch (ClassNotFoundException e) {
} finally {
try {
if (ois != null) ois.close();
} catch (IOException e) {
}
}
return retorno;
}
public void removeObjectFromFile(FileInputStream arqPrincipal,
Object objetoARemover, Context contexto) {
try {
// Construct the new file that will later be renamed to the original
// filename.
ObjectOutputStream oos = new ObjectOutputStream(
contexto.openFileOutput("dados.jlog.temp",
contexto.MODE_APPEND));
ObjectInputStream ois = new ObjectInputStream(arqPrincipal);
Object obj = null;
// Read from the original file and write to the new
// unless content matches data to be removed.
try {
while ((obj = ois.readObject()) != null) {
if (!(objetoARemover.equals(obj))) {
oos.writeObject(obj);
oos.flush();
}
}
} catch (EOFException eof) {
} finally {
oos.close();
ois.close();
// Delete the original file
File aDeletar = contexto.getFileStreamPath("dados.jlog");
File aRenomear = contexto.getFileStreamPath("dados.jlog.tmp");
if (!aDeletar.delete()) {
return;
} else {
// Rename the new file to the filename the original file
// had.
if (!aRenomear.renameTo(aDeletar)) Log.d(TAG_NAME,
"Error renaming file");
else Log.d(TAG_NAME, "Renaming successful");
}
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
Log.d(TAG_NAME, "Arquivo não encontrado");
} catch (IOException ex) {
ex.printStackTrace();
Log.d(TAG_NAME, "Erro de entrada/saída");
} catch (ClassNotFoundException e) {
Log.d(TAG_NAME, "Classe Não Encontrada.");
}
}
The method readObjectFromFile() seems to work just fine. I can even convert the read Object to Funcionario class and read its data.
My problems appear when I use removeObjectFromFile(). The idea is to create a temporary file to store objects from "dados.jlog" file other than the one that has been already loaded in the main program, then once this temp file is created the file "dados.jlog" should be deleted and the temporary file should be renamed to replace it.
The first thing I found out to be strange here is that the ois.readobject() keeps throwing an EOFException. While this makes sense, the tutorial I read on the internet doesn't mention this error. In fact, their code indicates that when the readObject() method reaches the EOF, it would return a reference to null, but instead this class throws this EOFException. I handled this exception in the code - though I'm not sure if this would be the right way to do it.
Another thing I find strange is the fact that this code fails to recognize the object that it should NOT copy. When I compare the object read from the file to the one received as argument, no matter what I try ( == , equals(), etc) they seem different objects to the compiler. Funcionario class is serializable has a serialversionUID, so the object read from the file should be identical to the one I stored. Worse than this, these 2 Objects being compared are read from the same file. They should be identical, right?
After creating the temporary file, I try to delete the original file and rename the temporary file. Though this seems to be working, once the removeObjectFromFile() ends the first time, the program is unable to read the data from the file "dados.jlog" again. I can't read the remaining data from the file and the program enters on an endless loop - since the 1st object is never removed from the list in the file.
Please enlighten me with this matter.
Personally I'd use an SQLLite database. Store each object in a row in the database. Once you've successfully transmitted you can remove the row from the database.
You can even reuse most of your code that you've already done. The easiest way to get there from where you are is to use a separate file for each object and store only the filename of the object in the database. You can then iterate over the rows in the database. Each time you transmit an object to your server simply delete that row from the database (and remove the file from the filesystem!). No rows in the database means no objects remain to be transmitted.

Categories