This question already has answers here:
Java serialization, UID not changed. Can I add new variables and method to the class?
(4 answers)
Closed 2 years ago.
I have a class with name and age
class Person implements serializable {
int age;
String name;
}
I have an ArrayList of this class
ArrayList<Person> persons = new ArrayList<Person>
When tomcat shuts down, I save the above to a local file:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
try {
out = new ObjectOutputStream(bos);
out.writeObject(persons);
out.flush();
byte[] objBytes = bos.toByteArray();
File file = new File("./persons.bin");
Files.write(objBytes, file);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
bos.close();
} catch (IOException ex) {
}
}
and when tomcat starts up, I load the file back:
File file = new File("./persons.bin");
if (!file.exists()) return;
byte[] objBytes = Files.toByteArray(file);
ByteArrayInputStream bis = new ByteArrayInputStream(objBytes);
ObjectInput in = null;
try {
in = new ObjectInputStream(bis);
ArrayList<Person> persons = (ArrayList<Person>) in.readObject();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
}
}
The above works as expected. My question is what will happen if later on the Person class will be added with new properties, for example:
class Person implements serializable {
int age;
String name;
String address;
}
I'm not using Gson because of the overhead it creates and I prefer to save the data as binary. Will the above fail? Succeed but address will be null? Weird things will happen? Do I have any control over what can be done in such cases?
Thanks
Edit: For the class I have
private static final long serialVersionUID = 1L;
It will break! You have to define a serialVersionUID or otherwise one will be created based on the properties of the class. So if you change the class the serialVersionUID would change aswell. If the UID don't match InvalidClassException is thrown.
Saving objects instead of the state of the object comes with alot of problems through and I recommend to save them in a more appropriate format. JSON, XML or even CSVs... Seriously that bit of overhead will save you from alot of trouble later on.
Related
I have an ArrayList filled with some custom POJO's that I'd like to persist when the user switches screen orientation (onSaveInstanceState) and when e.g. the user hits the back button (onPause).
As far as I know, the SharedPreferences can only hold primitive data types and bundles can not hold references to generic ArrayLists.
Any tips on how to tackle this?
Regards,
Marcus
1- create a class and put everything you want to store for example arraylist of your POJO and make that class implement Serializable interface.
class MyBundle implements Serializable {
ArrayList<POJO> mPOJO;
MyBundle( ArrayList<POJO> pojo){
mPOJO= pojo;
}
}
2- write it to file:
ObjectOutputStream oos = null;
FileOutputStream fout = null;
try{
FileOutputStream fout = new FileOutputStream("Your file path");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(mb); // mb is an instance of MyBundle
} catch (Exception ex) {
e.printStackTrace();
}finally {
if(oos != null){
oos.close();
}
}
and to get back everything:
ObjectInputStream objectinputstream = null;
try {
streamIn = new FileInputStream("Your file address");
objectinputstream = new ObjectInputStream(streamIn);
MyBundle mb = (MyBundle) objectinputstream.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(objectinputstream != null){
objectinputstream .close();
}
}
i don't know this is correct method or not but i handle this like this this always success while your app lost all cache data itself then also u can get back serializable object->
for generic ArrayLists always use serializable
just look at once http://developer.android.com/reference/java/io/Serializable.html
Try converting the List to Json using Gson or Jackson.
Store the string in the Shared preference. some thing like below code
String listString = gsonSD.toJson(list<object> instance);
SharedPreferences storeDataPref = getContext().getSharedPreferences("list_store_pref", Context.MODE_PRIVATE);
Editor storeDataEditor = storeDataPref.edit();
storeDataEditor.putString("liststringdata", listString).apply();
I have an object that has a List each ObjectA has a few data members (strings and a byte[]). Along with a FileInputStream used to read a file into the byte array. I have initialized all data members in the global scope of the object so that I can recycle them to reduce the amount of object creation per function call. When I serialize the object my FileInputStream is null, as I store the file in the byte array. so I expect that the FileInputStream being null would be skipped by the serialization process. both the main object and the objects that are put in the list of the main object are implementing serializable
I'm able to serialize the object that holds the list of objects and deserialize it as long as the list is empty. When the list has at least one object in it. It's still able to serialize but when i attempt to deserialize it I get the following error.
IOException: writing aborted; java.io.NotSerializableException: java.io.FileInputStream
The variables in my object's is as followed:
public class MainObject implements Serializable{
private String name;
private List<ObjectA> obj;
}
public class ObjectA implements Serializable{
private String id;
private String name;
private File fileStream;
byte []data;
}
To trouble shoot this I saved the serialized object to a file and looked at it and I can see the MainObject is being saved. If I include objects into the list the objects are also being saved.
Here is the code I made for reading the files and adding them to the object.
File[] files = new File(filePath).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println(file.getName());
try {
fin = new FileInputStream(filePath+file.getName());
ois = new ObjectInputStream(fin);
mainObjectList.add((MainObject) ois.readObject());
ois.close();
fin.close();
} catch (FileNotFoundException e) {
System.err.println("FileNotFoundException: " + e.getMessage());
} catch (ClassNotFoundException e) {
System.err.println("ClassNotFoundException: " + e.getMessage());
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
here is the setFile() that's in MainObject
public void setFile(String filePath) {
try {
File file=null;
fileStream = new FileInputStream(file=new File(filePath));
data = new byte[(int)file.length()];
fileStream.read(data,0,data.length);
for (int X : data){
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
Read the exception message. Contrary to the code you posted, you have a FileInputStream member, which is not serializable. Make it transient, or remove it, and construct it when you need it from the File.
And contrary to your claim that 'When i serialize mainObject and save it to a file i can see its saving all the data including the list of audioClip objects', when you serialized this data you got an exception, which you have ignored.
NB Your newly posted code:
try {
File file=null;
fileStream = new FileInputStream(file=new File(filePath));
data = new byte[(int)file.length()];
fileStream.read(data,0,data.length);
for (int X : data){
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
is very poor quality. It should have been written like this:
try (FileInputStream fileStream = new FileInputStream(new File(filePath))) {
data = new byte[(int)file.length()];
int total = 0;
int count;
while ((count = fileStream.read(data, total, data.length-total)) > 0) {
total += count;
}
for (byte X : data) {
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
Note that you cannot assume either that a file fits into memory, that the size fits into an int, or that read() fills the buffer. You have to:
store the result of read() into a variable
test it for -1, indicating end of stream
otherwise use it as the read count, instead of the buffer size
repeat until end of stream.
Note also that you don't need the File file variable at all; that the FileInputStream should always have been a local variable; and that you weren't closing it: this code does, via the try-with-resources syntax.
I have admin account which should be able to add many users to a .dat file. Then I want to retrieve all the objects from the .dat file into a list for further programming.
public class User implements Serializable { //get and set methods }
This is hwo I am writing each object to the .dat file
public void addNewUser() throws Exception {
User newUser=new User();
newUser.name="test";
newUser.position="admin";
FileOutputStream outStream = new FileOutputStream("Users.dat", true);
ObjectOutputStream objectOutputFile = new ObjectOutputStream(outStream);
// Write the object to the file.
objectOutputFile.writeObject(newUser);
// Close the file.
objectOutputFile.close();
}
How can retrieve all the objects from the .dat file into ArrayList??
public class displayUsers { **//what to do??** }
You can either write the list object and read it as list. But since you're writing user objects individually, you can do something like this -
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Users.dat"));
Object object = null;
while ((object = ois.readObject()) != null) {
if (object instanceof User) {
User user = (User) object;
list.add(user);
}
}
Of course, you would need to take care of exceptions (like EOFException).
Generally it is bad practice to concatenate individual ObjectOutputStreams in a file without adding any lengths or delimiters. So better write all objects in one pass (and use ObjectOutputStream.reset in case your process is long-running and you fear memory leaks (otherwise ObjectOutputStream will keep a reference to every object it serialized before) or add them to a List and write it.
If you have to write it in multiple passes, I'd suggest to write the individual objects to a ByteArrayOutputStream first, and then use DataOutputStream to write the array prefixed by its length. That way, you can use DataInputStream to get out the individual byte arrays and use ByteArrayInputStream to deserialize them.
In case this does not work, you can try this solution (depending on the lookahead used by ObjectInputStream, this might not work for more complex objects with custom serialization formats, though, so use at your own risk):
public static void displayUsers() throws Exception {
FileInputStream fiis = new FileInputStream("Users.dat");
InputStream fis = new FilterInputStream(fiis) {
#Override
public void close() throws IOException {
// ignore
}
};
try {
while (true) {
ObjectInputStream in = new ObjectInputStream(fis);
User user = (User) in.readObject();
in.close();
System.out.println(user.name + "/" + user.position);
}
} catch (EOFException ex) {
// done
}
fiis.close();
}
List<User> listOfUser = new ArrayList<User>();
ObjectInputStream input = null;
try {
while (true) {
input = new ObjectInputStream(new FileInputStream("Users.dat"));
listOfUser.add(input.readObject());
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
input.close();
}
I have an Object called Doodle, I serialize it into a String and it's ok.
The problem arrises when I try to deserialize the object, the error is this:
java.io.InvalidClassException: java.util.ArrayList; local class incompatible: stream classdesc serialVersionUID = 8664875232659988799, local class serialVersionUID = 8683452581122892189
The methods to serialize and deserialize are the following:
public static String serDoodle(Doodle dood){
String serializzato = null;
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream so = new ObjectOutputStream(bo);
so.writeObject(dood);
so.flush();
serializzato = bo.toString();
so.close();
bo.close();
} catch (Exception e) {
System.out.println(e);
}
return serializzato;
}
public static Doodle deserDoodle(String deserializza){
Doodle dod = new Doodle();
try {
byte[] b = deserializza.getBytes();
ByteArrayInputStream bi = new ByteArrayInputStream(b);
ObjectInputStream si = new ObjectInputStream(bi);
dod=(Doodle) si.readObject();
si.readObject().getClass();
si.close();
bi.close();
} catch (Exception e) {
System.out.println("deserDoodle "+e);
}
return dod;
}
I use the same method(but with different variable) to serialize another type of object and with that one it works greatly.
I don't understand where is the trouble!
I serialize it into a String and it's ok
No, it isn't OK. String is not a container for binary data. The round-trip between byte-array and String isn't guaranteed to be losses. Don't do this. Use byte[], or at least Base64-encode it.
I want to save ArrayList<CustomClass>-object to somewhere in Android storage to retrieve quickly and display data in it.
Is this possible or not? If yes, then which technique will be suitable, SQLite or external storage?
example.
public class MyClass implements Serializable
{
private static final long serialVersionUID = 1L;
public String title;
public String startTime;
public String endTime;
public String day;
public boolean classEnabled;
public MyClass(String title, String startTime, boolean enable) {
this.title = title;
this.startTime = startTime;
this.classEnabled = enable;
}
public boolean saveObject(MyClass obj) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
boolean keep = true;
try {
fos = new FileOutputStream(suspend_f);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
} catch (Exception e) {
keep = false;
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
if (keep == false) suspend_f.delete();
} catch (Exception e) { /* do nothing */ }
}
return keep;
}
public MyClass getObject(Context c) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
MyClass simpleClass= null;
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream(suspend_f);
is = new ObjectInputStream(fis);
simpleClass = (MyClass) is.readObject();
} catch(Exception e) {
String val= e.getMessage();
} finally {
try {
if (fis != null) fis.close();
if (is != null) is.close();
} catch (Exception e) { }
}
return simpleClass;
}
and calling from activity
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"MyCustomObject");
else
cacheDir= getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
MyClass m = new MyClass("umer", "asif", true);
boolean result = m.saveObject(m);
if(result)
Toast.makeText(this, "Saved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error saving object", Toast.LENGTH_LONG).show();
MyClass m = new MyClass();
MyClass c = m.getObject(this);
if(c!= null)
Toast.makeText(this, "Retrieved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error retrieving object", Toast.LENGTH_LONG).show();
dont forget to use write_external_storage permissions in manifest file.
This problem isn't really android specific. I mean if you know how to serialize your data either via java.io.Serializable or you have a custom persistence format, you just need to know where to store it.
You can grab a file on the local device via
FileOutputStream stream = null;
try{
stream = mContext.openFileOutput("name.data", Context.MODE_PRIVATE);
ObjectOutputStream dout = new ObjectOutputStream(stream);
dout.writeObject(myArrayList);
dout.flush();
stream.getFD().sync();
} catch(IOException e) { //Do something intelligent
} finally {
stream.close();
}
You will have to use openFileInput() later to read the data back.
Or you can grab External Storage. This is similar however you have to guarantee that it even exists. Like is the external storage connected and even able to be written to. Since you are writing a data structure here and usually external storage is world readable I don't think this is a good idea for your intended purposes (just from what you have put so far).
If your data is structured and is going to be queried many times and it might be rather big to always load up then consider using the sql lite tools that are part of the OS. However I am assuming you don't need this either as a simple list is just that, a linear structures which you could probably seek through in a file (assuming it is less than 1MB of data :)
This issue can be solved by singleton class here in which you can set/get any object eg arrayList in any time. If you used Android Service then this solutuion is not suitable.
Perhaps the solution to your problem can be found here.