Serialize a hierarchical structure of objects in java - java

I have an abstract class Screen and child classes: GameScreen, SpellScreen, StatsScreen, etc.
The game works in this way: a Renderer class creates a root
Screen screenRoot = new GameScreen()
which then is free to add screens to itself, which then may add screens to themselves and so it goes. Therefore a tree-like structure is formed, every Screen containing a list of its child-screens.
Now I am wondering if it's possible to perform serialization and deserialization on that - I'd like to recreate all the screens in the same hierarchy.
Is it enough to serialize only the screenRoot object, and then deserialize it (provided I want to preserve the whole screens tree), or do I need to traverse the tree myself somehow?
How would you go about serializing this?
P.S. the game is for Android and uses OpenGL ES 2.0.

A hierarchy of objects is no impediment to using Java Serialization, as the latter can cope with arbitrary object graphs - and yes, serializing an object using Java Serialization will serialize all objects it refers to (unless that reference is marked transient). Assuming that's what you want, serializing the hierarchy is as simple as:
try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(filename)))) {
oos.write(rootScreen);
}
and reading as simple as:
try (ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(filename)))) {
return (GameScreen) ois.readObject();
}

There are two issues here.
First, screens should be just that--screens. They shouldn't contain the "model" or object data that represents your game state; only the view/rendering of that state. So serializing and deserializing, doesn't really make sense. I would suggest looking at your architecture again to see if this is really what you want to do.
If you decide to do it, or if you have another game-state object root that you can serialize (I usually use the Player since it has all the essential data in it), you can easily do this with Gson:
// Save
RootObject o = ...; // The root of the hierarchy to serialize
Gson gson = new Gson();
String serialized - gson.toJson(o); // JSON object, eg. { "Player": { ... } }
// Load
RootObject deserialized = gson.fromJson(serialized, RootObject.class);
You can read more in their user guide.
Second, on the issue of JSON and Gson: I prefer this over standard serialization, because it's robust in the face of changes. If your class definitions change, you can still deserialize objects (albeit you get null/empty fields) instead of a runtime exception; you don't need to worry about versioning your classes, either.
Edit: questions like this are better suited to the Game Dev SE site.

Related

How can I lazy load specific elements of a JSON file efficiently with GSON?

I have a JSON file that is marshalled into custom object using GSON.
All works fine.
An example of the structure I have:
public class Domain {
private String id;
private String name;
//other fields
private ArrayList<Structures> structs;
private ArrayList<Buildings> buildings;
private ArrayList<CustomObject> objects;
// some more lists and fields
}
So basically I create a builder and parse the json
Gson gson = new GsonBuilder().create();
gson.fromJson(jsonString, Domain.class);
This works absolutely fine. Parsing is done and I get my data.
Problem:
I don't really need to have various fields of the Domain class populated from the start because e.g. I may have the Domain class with a lot of elements in the e.g. list for structs but I might not really need to access them.
What I need to do is some kind of pattern for lazy loading.
I.e. I would like to not load some parts of class during the json parsing and only load them when needed.
Question:
If I understand correctly the way to skip fields from being parsed is by making them transient.
But then if at some later time I need to access e.g. the structs how would I actually load them at that point? I think that reloading/reparsing all the json again is suboptimal.
What is a standard/good approach for this?
This is a really lengthy topic. There are many approaches to this and all of them are usually a lot more complicated. The easiest one, if you really value something very simple for me was so far not using gson, but for example something like JSONObject and then populate the object myself. using this you could easily split this up into multiple steps. The problem that now arises is, that you never know, what exactly is already loaded - or more - what is maybe loaded, but just not filled as a field.
Lazy loading using automatic conversions like gson is unfortunately always gonna involve unnecessary object creation too, so question then is if its not less pain just to do it yourself from the beginning.
if it has to be gson, you could have different objects for different stages. read them in through json and then apply to your main object.
a favourable version of that is probably to split up the object into different components (or aspects or whatever you want to call it) that match the different loading stages. Different possibilities but lets just pick one of them:
class Domain {
private String id;
private DomainStructs domainStructs;
}
class DomainStructs {
private ArrayList<Structures> structs;
}
Now you need a new Object in this version of doing this. This means the overall size of the model is slightly (but not much really) bigger and you should probably match together things that are necessary together anyway - so not load every field separate, but this way you can leave out parts and easily add them later by populating them from Gson like 2 steps here:
Gson gson = new GsonBuilder().create();
Domain domain = gson.fromJson(jsonString, Domain.class); // first time
domain.structs = gson.fromJson(jsonString, DomainStructs.class); // now adding
I am not saying this is the best idea ever, but it fulfills your idea while still using gson.
I would though consider splitting up the Data already - so not storing the strings, but holding the data in different components in this case if it is possible. Basically you want a domainJsonString and a domainStructsJsonString if you get what i mean. stored in a way so you can easily retrieve them separately.
I hope this helps you to move a bit forward

How can I save and load a level file with abstract objects?

I'm trying to make a level editor that provides a save functionality like mario maker - the user can create a level and save the level data. How is this usually done? Specifically what I'm struggling with is my level contains a list of Enemies (an abstract class). When I write to a file I can write the json representation of the concrete enemy classes, but when I read from the file, I'd need to know what concrete class it is in order to reconstruct it into that specific class. That seems like a pain - I'd have to manually add some code to write out what class type the enemy is when it gets saved and also add code to read what class type and create an instance of that class when read. I'm afraid of maintaining that for every new Enemy that I create. So my main question is how can I most easily read a list of concrete Enemies into a list of abstract Enemies? It seems like some knowledge about the class is required on save/load.
Also, is saving as JSON the way to go here or is serialization better? Does it matter?
Since your going to be creating concrete classes when the program starts up, you will need to know the actual class of each one. There are a bunch of ways you could do this. To do something easy, you could add a getLabel() method to each concrete class and use that as a switch to figure out the correct concrete class.
// Using jackson-databind
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readValue(json, JsonNode.class);
Enemy enemy = null;
if (GOOMBA_LABEL.equals(node.get("label").asText()))
enemy = mapper.readValue(json, Goomba.class);
I really like using JSON libraries functionality to parse my JSON into a POJO. However, doing the above would actually require double parsing - 1) Parse into some generic structure (like a Map or JsonNode), check the label and then 2) Parse into POJO.
Another thing you could do is prepend a "Magic Number" before each JSON string to let you know which type it is. Then you don't have to double parse JSON.
DataInput input = new DataInputStream(fileInputStream);
int magic = input.readInt();
Enemy enemy = null;
if (GOOMBA_MAGIC == magic) {
String json = input.readUTF();
enemy = mapper.readValue(json, Goomba.class);
}
As far as if JSON is the right serialization to use, it's up to you. The things that are nice about it is it's human readable and editable. There are other serialization technologies if performance or disk usage are more important to you. For something like this, JSON seems fine.

Save java object tree with versioning?

I have a Java application which saves a object tree to file using default java object serialization (ObjectOutputStream and such). This works quite nicely.
The problem arises when I add a field to an object that is in the object tree. When I now load the old object tree I get a ClassException, which of course makes sense cause that object/class changed and the old class doesn't match the new class.
Now I can write different versions of my load method so that it can handle old object trees, but I suspect this might become not very easy to manage and maintain.
I was wondering if there is a better way to save a object tree in java which supports versioning?
You can use this approach (implementing readObject and writeObject) to handle schema migration: http://www.javaworld.com/javaworld/jw-02-2006/jw-0227-control.html
use the readObject()
and writeObject()
methods of ObjectInputStream and ObjectOutputStream class in your Serializable class
in order to define the default behavior when serializing/deserializing the object from
your file
ObjectOutputStream oos = new ObjectOutputStream(file/path/url);
oos.writeObject(serialized Object);
oos.close();
oos.flush();
ObjectInputStream ois = new ObjectInputStream(file/path/url/request);
Object obj = (TypeCast to appropriate Object)ois.readObject();

Transfer of a Java Serialized Object

Is it possible to declare an instance of a serializable object in one Java program / class, then repeat the definitions of the internal objects in a different program /class entirely, and load in a big complex object from a data file? The goal is to be able to write an editor for items that's kept locally on my build machine, then write the game itself and distribute it to people who would like to play the game.
I'm writing a game in Java as a hobbyist project. Within my game, there's an a family of classes that extend a parent class, GameItem. Items might be in various families like HealingPotion, Bomb, KeyItem, and so on.
class GameItem implements Serializable {
String ItemName
String ImageResourceLocation
....}
What I want to do is include definitions of how to create each item in a particularly family of items, but then have a big class called GameItemList, which contains all possible items that can occur as you play the game.
class GameItemList implements Serializable {
LinkedList<GameItem>gameItemList;
//methods here like LookUpByName, LookUpByIndex that return references to an item
}
Maybe at some point - as the player starts a new game, or as the game launches, do something like:
//create itemList
FileInputStream fileIn = new FileInputStream("items.dat");
ObjectInputStream in = new ObjectInputStream(fileIn);
GameItemList allItems = (GameItemList)in.readObject();
in.close();
//Now I have an object called allItems that can be used for lookups.
Thanks guys, any comments or help would be greatly appreciated.
When you serialize an object, every field of the object is serialized, unless marked with transient. And this behavior is of course recursive. So yes, you can serialize an object, then deserialize it, and the deserialized object will have the same state as the serialized one. A different behavior would make serialization useless.
I wouldn't use native serialization for long-term storage of data, though. Serialized objects are hard to inspect, impossible to modify using a text editor, and maintaining backward compatibility with older versions of the classes is hard. I would use a more open format like XML or JSON.
Yes, that is possible. If an object is correctly serialized, it can be deserialized in any other machine as long as the application running there knowns the definition of the class to be deserialized.
This will work, but Java serialization is notorious for making it hard to "evolve" classes -- the internal representation is explicitly tied to the on-disk format. You can work around this with custom reader / writer methods, but you might consider a more portable format like JSON or XML instead of object serialization.

Implementing my own serialization in java

How can I implement serialization on my own. Meaning I don't want my class to implement serializable. But I want to implement serialization myself. So that without implementing serializable I can transfer objects over network or write them to a file and later retrieve them in same state. I want to do it since I want to learn and explore things.
Serialization is the process of translating the structure of an object into another format that could be easily transfered across network or could be stored in a file. Java serializes objects into a binary format. This is not necessary if bandwidth/disk-space is not a problem. You can simply encode your objects as XML:
// Code is for illustration purpose only, I haven't compiled it!!!
public class Person {
private String name;
private int age;
// ...
public String serializeToXml() {
StringBuilder xml = new StringBuilder();
xml.append("<person>");
xml.append("<attribute name=\"age\" type=\"int\">").append(age);
xml.append("</attribute>");
xml.append("<attribute name=\"name\" type=\"string\">").append(name);
xml.append("</attribute>");
xml.append("</person>");
return xml.toString();
}
Now you can get an object's XML representation and "serialize" it to a file or a network connection. A program written in any language that can parse XML can "deserialize" this object into its own data structure.
If you need a more compact representation, you can think of binary encoding:
// A naive binary serializer.
public byte[] serializeToBytes() {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
// Object name and number of attributes.
// Write the 4 byte length of the string and the string itself to
// the ByteArrayOutputStream.
writeString("Person", bytes);
bytes.write(2); // number of attributes;
// Serialize age
writeString("age", bytes);
bytes.write(1); // type = 1 (i.e, int)
writeString(Integer.toString(age), bytes);
// serialize name
writeString("name", bytes);
bytes.write(2); // type = 2 (i.e, string)
writeString(name, bytes);
return bytes.toByteArray();
}
private static void writeString(String s, ByteArrayOutputStream bytes) {
bytes.write(s.length());
bytes.write(s.toBytes());
}
To learn about a more compact binary serialization scheme, see the Java implementation of Google Protocol Buffers.
You can use Externalizable and implement your own serialization mechanism. One of the difficult aspects of serialization is versioning so this can be a challenging exercise to implement. You can also look at protobuf and Avro as binary serialization formats.
You start with reflection. Get the object's class and declared fields of its class and all superclasses. Then obtain value of each field and write it to dump.
When deserializing, just reverse the process: get class name from your serialized form, instantiate an object and set its fields accordingly to the dump.
That's the simplistic approach if you just want to learn. There's many issues that can come up if you want to do it "for real":
Versioning. What if one end of the application is running new version, but the other end has an older class definition with some fields missing or renamed?
Overwriting default behavior. What if some object is more complex and cannot be recreated on a simple field-by-field basis?
Recreating dependencies between objects, including cyclic ones.
... and probably many more.
Get the Java Source code and understand how Serialization is implemented. I did this some month ago, and now have a Serialization that uses only 16% of the space and 20% of the time of "normal" serialization, at the cost of assuming that the classes that wrote the serialized data have not changed. I use this for client-server serialization where I can use this assumption.
As a supplement to #Konrad Garus' answer. There is one issue that is a show-stopper for a full reimplementation of Java serialization.
When you deserialize an object, you need to use one of the object's class's constructors to recreate an instance. But which constructor should you use? If there is a no-args constructor, you could conceivably use that. However, the no-args constructor (or indeed any constructor) might do something with the object in addition to creating it. For example, it might send a notification to something else that a new instance has been created ... passing the instance that isn't yet completely deserialized.
In fact, it is really difficult replicate what standard Java deserialization code does. What it does is this:
It determines the class to be created.
Create an instance of the class without calling any of its constructors.
It uses reflection to fill in the instance's fields, including private fields, with objects and values reconstructed from the serialization.
The problem is that step 2. involves some "black magic" that a normal Java class is not permitted to do.
(If you want to understand the gory details, read the serialization spec and take a look at the implementation in the OpenJDK codebase.)

Categories