I try to write a keyholder, and I want to write the passwords to a .dat file using ObjectOutputStream, and then read them using ObjectInputStream. This is my code for writing the objects:
public void toFile()
{
try
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("passwords.dat"));
for(int i = 0; i<this.nrOfPW; i++)
{
if(this.PWlist[i] instanceof longPW)
{
oos.writeObject((longPW)this.PWlist[i]);
}
else
{
oos.writeObject((PinPW)this.PWlist[i]);
}
}
oos.close();
}
catch(IOException e)
{
e.getStackTrace();
}
}
This seems to work, but when I try to read the file again and put the objects in my PWlist array it says that PinPW isn't serializable, even though PinPW implements Serializable and it's imported. The base class of PinPW (Info) also implements Serializable and imports it. This is the code where I read the file:
public void fromFile()
{
try
{
ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("passwords.dat"));
while(objIn.readObject() != null)
{
if(this.nrOfPW == this.PWlist.length)
{
expand(10);
}
if(objIn.readObject() instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
this.nrOfPW++;
}
}
objIn.close();
}
catch(EOFException e)
{
e.getStackTrace();
}
catch(IOException ex)
{
ex.printStackTrace();
}
catch(ClassNotFoundException ex)
{
ex.printStackTrace();
}
}
The PWlist array is a Info array, and PinPW and longPW extends Info.
What do I do to fix this problem?
Let's fix the "first bug, first" ...
In this code:
while(objIn.readObject() != null) // reads object, tests then *discards* it
{
...
if(objIn.readObject() instanceof PinPW) // reads object, tests then *discards* it
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject(); // conditionally read an object
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject(); // conditionally read an object
this.nrOfPW++;
}
}
Each time around your loop iteration, you actually read 3 objects. The first time you read an object to check there was one in the stream, the next time you read one and determine it's type, then discard it. Then you read a third object and cast it based on what the type of the discarded object was.
In addition, as EJP correctly points out, the correct way to determine End of Stream for an ObjectInputStream is to catch the end of file exception.
You want to do this instead:
try
{
while (true)
{
final Object o = objIn.readObject(); // read the object from the stream
...
if (o instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW) o; // cast to correct type
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW) o; // cast to correct type
this.nrOfPW++;
}
}
}
catch (EOFException e)
{
// end of stream reached ...
// ... close the file descriptor etc ...
}
You have a problem here.
while(objIn.readObject() != null)
{
if(this.nrOfPW == this.PWlist.length)
{
expand(10);
}
if(objIn.readObject() instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
this.nrOfPW++;
}
}
You are reading an object many times. Try to save it and then work with it.
if(objIn.readObject() instanceof PinPW) reads one, reads twice, this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject(); reads three times, when it should be only once.
PS: use Greg Kopff syntax inside a while and without the final keyword because you want to save more objects in it.
I just wanted to point out the if-else block in the toFile() function is completely pointless. writeObject() takes an Object argument. It doesn't care what type of Object it is, as long as it's serializable.
While your fromFile() method is seriously flawed, it would not cause a NotSerializableException. I believe the exception you're referring to actually happened in toFile().
The cause is very simple: you didn't fully read and understand the documentation of ObjectOutputStream. To be specific, the Object and all its non-transient fields and all non-transient fields in its ancestor classes has to implement the Serializable. It must also have a public no-arg constructor.
Related
I am making a voice chat program and I got the OptionalDataException error and I never had this problem with the code before I added voice. The voice communication is handles by a different socket so I don't see the problem.
Code:
public class Client implements Runnable { // CLIENT
private String msg;
public void run() {
try {
s1 = new Socket(ipAddress, port);
s2 = new Socket(ipAddress, 1210);
o1 = new ObjectOutputStream(s1.getOutputStream());
o1.writeObject(name);
serverListModel.addElement(name);
i1 = new ObjectInputStream(s1.getInputStream());
Thread voice = new Thread(new ClientAudio());
voice.start();
while(true) {
msg = (String) i1.readObject();
String[] namePart = msg.split("-");
if(namePart[0].equals("AddName") && !namePart[1].equals(name) && !serverListModel.contains(namePart[1])) {
serverListModel.addElement(namePart[1]);
}
if(namePart[0].equals("RemoveName") && !namePart[1].equals(name)) {
serverListModel.removeElement(namePart[1]);
}
if(!msg.equals(null) && !namePart[0].equals("AddName") && !namePart[0].equals("RemoveName")) {
chatWindow.append(msg+"\n");
}
}
} catch (IOException | ClassNotFoundException e) {
chatWindow.append("Server Closed");
e.printStackTrace();
try {
s1.close();
} catch (IOException e1) {
e1.printStackTrace();
}
mainWindow(true);
}
}
}
flag
it was thrown at msg = (String) i1.readObject(); and it says
java.io.OptionalDataException
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1361)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at client.chat$Client.run(chat.java:319)
at java.lang.Thread.run(Thread.java:745)
From Oracle:
Exception indicating the failure of an object read operation due to unread primitive data, or the end of data belonging to a serialized object in the stream. This exception may be thrown in two cases:
An attempt was made to read an object when the next element in the stream is primitive data. In this case, the OptionalDataException's length field is set to the number of bytes of primitive data immediately readable from the stream, and the eof field is set to false.
An attempt was made to read past the end of data consumable by a class-defined readObject or readExternal method. In this case, the OptionalDataException's eof field is set to true, and the length field is set to 0.
It would appear that the next object in the Stream is not a String.
Is the server code under your control? Or do you at least have the source? If so, verify that String objects are the only ones being transmitted, or adjust your code to handle the actual objects/primitives being sent.
Edit
From your other question Voice Server not working:
byte[] soundData =
//...
o.write(soundData, 0, bytesRead);
... It looks like you are not writing String objects to the ObjectOutputStream. Actually, not even writing an Object, but raw bytes. You have to read data the same way you write it; anything else just won't work.
I have a block of code, that deserializes multiple objects from file. How can i avoid using a while(true)?
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
filename));
while (true) {
try {
MyObject o = (MyObject) in.readObject();
// Do something with the object
} catch (EOFException e) {
break;
}
}
in.close();
You should write either a collection (with a size), or a put a marker before each object:
try {
for (;in.readBoolean();) {
MyObject o = (MyObject) in.readObject();
}
} catch (EOFException e) {
// ...
}
When you write your object, write a boolean just before (it will however take 1 byte if I do remember well that part):
for (MyObject o : iterable) {
out.writeBoolean(true);
out.writeObject(o);
}
out.writeBoolean(false);
If iterable is a collection or map, you can use default serialization:
out.writeObject(iterable); // default collection serialization
Beside, don't catch an exception for each item, catch it globally (especially EOFException!): it is better for performance reasons.
I don't know if you work with Java 7, but your code + my for loop can be written like this:
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream( filename))) {
for (;in.readBoolean();) {
MyObject o = (MyObject) in.readObject();
}
} catch (EOFException e) {
// ...
}
// no need to close, the try-with-resources do the job for you.
How can i avoid using a while(true)?
You can't.
More to the point, why do you think you want to?
This is a classic example of the tail wagging the dog. EOFException is thrown to indicate end of stream. Ergo you have to catch it, and ergo you have to loop until it is thrown, ergo you have to use while (true) or one of its cognates.
The exception thought police would have you prepend an object count, taking the curious position that external data structures should be designed to suit the coder's phobias, and overlooking that you may not know it in advance, or may need to change your mind, or may need to exit prematurely; or would have you write a null as an end-of-stream marker, overlooking that it prevents the use of null for any other purpose; and in both cases overlooking the fact that the API is already designed to throw EOFException, and already works the way it already works, so you already have to code accordingly.
The code that I'm proposing let you to serialize and deserialize multiple objects really easily without having any problems and avoiding the awful (in my opinion) while true:
public class EntityClass implements Serializable{
private int intVal;
private String stringVal;
public EntityClass(int intVal, String stringVal) {
this.intVal = intVal;
this.stringVal = stringVal;
}
#Override
public String toString() {
return "EntityClass{" +
"intVal=" + intVal +
", stringVal='" + stringVal + '\'' +
'}';
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
EntityClass a = new EntityClass(1, "1");
EntityClass b = new EntityClass(2, "2");
EntityClass c = new EntityClass(3, "3");
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("out"));
stream.writeObject(a);
stream.writeObject(b);
stream.writeObject(c);
stream.close();
ObjectInputStream streamRead = new ObjectInputStream(new FileInputStream("out"));
EntityClass[] entities = new EntityClass[3];
int cont = 0;
try {
while (streamRead.available() >= 0) {
entities[cont] = (EntityClass) streamRead.readObject();
System.out.println(entities[cont]);
cont++;
}
} catch (EOFException exp) {
} finally {
streamRead.close();
}
}
}
anyway to check if method readObject of class ObjectInputStream has finished reading file other than catching its thrown exceptions?
and if no. how can I make outNewmast.writeObject(accountRecord); statement reached in this case?
// read oldmast.ser
try {
while (true) {
accountRecord = (AccountRecord) inOldmast.readObject();
//read trans.ser
while (true) {
transactionRecord = (TransactionRecord) inTrans.readObject();
if (transactionRecord.getAccountNumber() == accountRecord.getAccount()) {
accountRecord.combine(transactionRecord);
}//end if
}//end inner while
outNewmast.writeObject(accountRecord);
}//end while
}//end try
catch (ClassNotFoundException e) {
System.err.println("Error reading file.");
System.exit(1);
}//end catch
catch (IOException e) {
System.err.println("Error reading file.");
System.exit(1);
}//end catch
The best idea would be to serialize the number of elements beforehand, so you could just do:
cnt = file.readInt();
for (int i=0;i<cnt;i++) {
file.readObject();
}
The method proposed by #ChrisCooper is not reliable, as stated in documentation. Some streams don't implement it, or return approximate result (in theory, it can even return 0 when there is still some data. Example - network stream).
Therefore, looking at same documentation, we find this particular block:
Any attempt to read object data which exceeds the boundaries of the
custom data written by the corresponding writeObject method will cause
an OptionalDataException to be thrown with an eof field value of true.
Non-object reads which exceed the end of the allotted data will
reflect the end of data in the same way that they would indicate the
end of the stream: bytewise reads will return -1 as the byte read or
number of bytes read, and primitive reads will throw EOFExceptions. If
there is no corresponding writeObject method, then the end of default
serialized data marks the end of the allotted data.
So, the best idea would be to catch an OptionalDataException and check it's eof field for true.
And to digest the answer even further, here's the method you want:
TransactionRecord readRecord(ObjectInputStream stream) throws OptionalDataException, IOException {
try {
transactionRecord = (TransactionRecord) stream.readObject();
} catch (OptionalDataException e) {
if (e.eof) {
return null;
} else {
throw e;
}
}
return transactionRecord;
}
.....
TransactionRecord record;
while ((record = readRecord(inTrans)) != null) {
doSomethingWithRecord(record);
}
endOfFile();
Yes, check the input stream to see if anything more is available:
http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html#available()
if (inOldmast.available() > 0) {
// read and process
} else {
// Close the stream and clean up
}
I have a series of different object serialized into a binary file.How can I read the file until the end?
try {
ObjectInputStream reader = new ObjectInputStream(new FileInputStream(fname));
Object obj = reader.readObject();
if (obj instanceof Azienda) {
Azienda a = (Azienda) obj;
company.put(a.getCod(), a);
} else if (obj instanceof Privato) {
Privato p = (Privato) obj;
privato.put(p.getCod(), p);
}
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch (FileNotFoundException ffe) {
System.err.println("Error: the file was not found!");
} catch (IOException ioe) {
ioe.printStackTrace();
}
in this way I read only once object per read.
when I read a text file I use null
try{
while(true) {
Object obj=reader.readObject()
// do sth with object
}
}catch(EOFException e){
//we expect it so ignore
}
there is no EOF check besides the exception when you read past it for ObjectInputStream so you'll have to use the code smell called exceptions for control flow
It seems that an EOFException is thrown when there is no more object to read from the stream. Unfortunately, it's not even documented. So, I see the following solutions:
you read in a loop until you get this exception,
you make it so that you know in advance the number of objects in the stream,
you make it so that there is a marker object which marks the last object of the stream,
you serialize (and unserialize) a unique object: a List<Object> containing all the objects. This last solution, of course prevents writing the objects on the fly to the stream, and forces you to have all the objects in memory before serializing them.
ObjectInputStream does not have a concrete method for checking for end-of-file.
But every read...() method of ObjectInputStream throws an EOFException when it tries to read past the end of file. Unfortunately this is not explicitly documented for readObject(), but it is for all the other methods (readInt() etc.)
http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html
ObjectInputStream reader = null;
try {
reader = new ObjectInputStream(new FileInputStream("sth"));
Object obj = null;
while ((obj = reader.readObject()) != null) {
System.out.println(obj);
}
} catch (EOFException e) {
System.out.println("finnished reading");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (FileNotFoundException ex) {
System.err.println("Error: the file was not found!");
} catch (IOException ex) {
ex.printStackTrace();
} finally {
reader.close();
}
I have created a method in my java assignment to write into a file from a LinkedList (I used serialization) , then I have created another method to read the file into the inkedList. The following is my method's body:
try {
FileInputStream fin = new FileInputStream("c:\\Info.ser");
ObjectInputStream ois = new ObjectInputStream(fin);
Employee e = (Employee) ois.readObject();
linkP.add(e);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
but it doesnt work right. I think this part:
Employee e = (Employee) ois.readObject();
linkP.add(e);
reads only the first object of the file into the linkedlist and ignores other objects. I surrounded it for loop and while loop several times but it causes EOFException. How can I change my method to read all of the file's objects into the LinkedList?
If you used LinkedList for serialization you should expect a LinkedList to deserialize:
linkP= (LinkedList) ois.readObject();
instead of
Employee e = (Employee) ois.readObject();
linkP.add(e);
The easiest way is to include the size of the list as the first thing written to the file. When you read the file, the first thing you retrieve is the size. Then you can read the expected number of objects.
Are you sure that the serialized file contains all of the elements? It looks to me like you might only be serializing one.
Note: Please also add the code where you create the info.ser file, since you may have corrupted the ObjectOutputStream by closing/reopening it for each object.
But to answer your question, the proper way of doing it (without catching exceptions) would be:
#Test
public void testSerializingListByEntries() throws Exception {
List<Serializable> list = new ArrayList<Serializable>();
list.add(new Date());
list.add(new Date());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeInt(list.size()); // Magic
for(Serializable o : list) {
oos.writeObject(o);
}
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
int count = ois.readInt();
List<Object> newList = new ArrayList<Object>();
for(int i = 0; i < count;i++) {
newList.add(ois.readObject());
}
ois.close();
assertEquals(list,newList);
}
Yes, you need to close the streams yourself of course. Omitted for readability.
Would probably need to see how you're writing in the first place but generally:
ObjectInputStream is = null;
try
{
is = new ObjectInputStream(new FileInputStream("c:/Info.ser"));
Object object = null;
while ((object = is.readObject()) != null)
{
linkP.add(object);
}
}
catch(Exception e)
{
//Whatever you need to do
}
finally
{
//Never forget to close your streams or you'll run into memory leaks
try
{
if (is != null)
{
is.close();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
Also, its probably better practice for you to handle the exceptions individually but I can't really tell what the streams throw so replace the (Exception e) with everything else.
surround Employee e = (Employee) ois.readObject();
linkP.add(e);
with a for loop as you suggested and surround the .readObject call with a try/catc(EOFException)
Just catch EOFException separately inside your reading loop and process it accordingly, i.e. break out of the loop.