How can I have more flexible serialization and deserialization in Java? - java

If I serialize an object in Java, and then later add an extra field to the java class, I can't deserialize the object into the modified class.
Is there a serialization library or some way that I can have deserialization be less strict, like if there is an extra field added to the class then it just fills that with null upon deserialization of the old version of the class?

You need to keep a serialVersionUID on your class. Check out the section "Version Control" in this article by Sun.

You've got lots of potential options.
You could use a graph serialisation library to define and manage your format e.g. Google's protocol buffers or Kryo. I believe both of these have built-in support for versioning.
You can write your own custom serialisation code and handle the versions explicitly - e.g. serializing to a flexible format like XML. When reading the XML you can configure it to use default values if a particular field isn't specified.
Or you could design your class in a "flexible" way, e.g. have all the fields stored in a HashMap and indexed by Strings. Depending on what you are trying to do, this may be a convenient option.

There's a fair few serialization libraries, take a look at Simple though:
http://simple.sourceforge.net/
or as mentioned above Google Protocol Buffers.
http://code.google.com/apis/protocolbuffers/

Implement Externalizable and you can do whatever you want. The puts the onus of serial/deserialization completely upon the class being serialized.

Did you add a serialVersionUID? This must be present (and unchanged) if you want to serialize / deserialize different Versions of a class.
Furthermore you can add the following two methods to your class to define exactly the serialization process:
private void writeObject(java.io.ObjectOutputStream stream)
throws IOException;
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException;
The Javadoc of ObjectInputStream gives more detail on its usage.

If I serialize an object in Java, and
then later add an extra field to the
java class, I can't deserialize the
object into the modified class.
That's untrue for a start. You need to have a good look at the Versioning section of the Object Serialization specification before you go any further.

Related

Tell Jackson about a new type with "content" for #JsonSerialize(contentUsing=...)

I am using a third-party library that defines an Option class, which is similar to java.util.Optional. In one case, it holds a type that needs a custom deserializer. With Optional, I could write
class StdContainer {
#JsonDeserialize(contentUsing=MyDeserializer.class)
#JsonSerialize(contentUsing=MySerializer.class)
Optional<MyClass> content;
}
and Jackson will using my custom serializer/deserializer on the class.
With the third party library, I tried writing
class ThirdPartyContainer {
#JsonDeserialize(contentUsing=MyDeserializer.class)
#JsonSerialize(contentUsing=MySerializer.class)
Option<MyClass> content;
}
This serializes MyClass using the default serializer, which makes sense, since I would not expect Jackson to know about the third-party library. Is there a way to tell Jackson that a particular class is a container and that it should use contentUsing on that class? I would expect that this also involves telling Jackson how to get the content.
The way Optionals (from Guava, Java 8, Scala) are recognized as containers is by them registering TypeModifier: this makes Jackson recognize them as somewhat special (actual type is ReferenceType).
To see how these work you could have a look at, say, jackson-datatype-guava. Once type is refined actual serializer/deserializer implementation is quite simple: jackson-databind has base implementations that provide about 90% of handling (see, for example AtomicReferenceSerializer and AtomicReferenceDeserializer in jackson-databind).
Use cases like these are actually big reason why Jackson module interface was added, to allow development and sharing of datatype modules for 3rd party types, so that once maintainers of a datatype lib (or community) provides such module, other users can simply plug it in and things "just work"

Alternatives to Reflection

I would like to implement my own Universal Binary JSON Specification-like specification. As one of my first steps I would like to have a method, which takes an object as parameter, like Gson does in its String com.google.gson.Gson.toJson(Object src) method. So I would like to know the fields and the data types of them. As far as I know does Gson use reflection to achieve that.
Another "solution" could be Serialization. But it's maybe tough to extract the fields from there, and I would prefer anyway, that it's not a must for the object to implement Serializable.
Is Reflection the way to go with my intentions?
You may consider bean introspection (see java.beans.Introspector) for a cleaner, albeit more limited approach.
If your objects are not pure beans (i.e. you need to map fields without getters or setters), you need to use reflection.

Java JSON deserialization without predefining everything

I realize this has probably been asked a hundred times but I have searched a lot and can't find specifically what I'm looking for.
Here is what I'd like. Given a string data, I'd like to deserialize into an object obj that doesn't have all the fields predefined. I'd like to just be able to ask for the fields I want such as obj.getString("stringFieldName") or obj.getInt("intFieldName"). I already have gson being used for other things so if it is possible with gson that would be great although not opposed to using another library.
The 'standard' Android JSON library (since API 1) already provides such untyped access.
See JSONObject, eg. getInt:
Returns the value mapped by name if it exists and is an int or can be coerced to an int, or throws otherwise.
Unless needing the JSON mapped onto a 'native' Java collection type this is probably the simplest way to achieve the request. It doesn't require any additional libraries.
With Jackson library you can annotate data model class with
#JsonIgnoreProperties(ignoreUnknown = true)
and the jacksonconverter will just parse only these fields that you defined. Other will be ignored.
Have you tried using Retrofit from Square? It works with GSON and Java Annotations and it's super easy to set up.

What's the best way to (de)serialize a Java object to file

I'd like to save a Java object to file (for unit testing later on). I have tried JSON but since I don't own the classes to the objects I'm serializing, deserialization becomes more effort than it is worth. (The getters and setters to the existing classes are overloaded with different types and Jackson cannot figure out how to deserialize the object)
Now I'm exploring other avenues (i.e. serializing to binary or some other format). I'm wondering if there is anything out there that can dump a Java object to binary/file so that deserialization is trivial. I understand you can do this with the Serializable interface, but again I don't own these classes which don't implement this interface so they cannot be modified.
You could also use XStream which does not depents on classes having implemented Serializable interface.
if they implement Serializable you do not need to own them (nor modify them), you can just write them to file using an ObjectOutputStream and read them back in with a ObjectInputStream
Assuming the classes have proper getters and setters you should have no issue doing this
You can also use Kryo. Benchmarks (here and here) say it is one of the fastest to serialize/deserialize and uses less space too. It also doesn't need Serializable to be implemented. That said, I have never used it personally.

How to log internals of an arbitrary object in Java?

I have a Java object with some unknown structure. Now I want to output this structure (properties and their values) to log file. And of course I'm interested to do this in recursive mode. Are there any libraries that can help me?
XStream is extremely good at printing object graphs, even handling cycles without any extra configuration or extra code in your classes (i.e. no messing with toString()'s). Just add the library and you can do this to anything and get nice, useful output:
log.debug("The object: {}", new XStream().toXML(anyObject));
That will give you XML output. If you prefer JSON, you can get it with a tiny bit more work as detailed in the XStream JSON tutorial.
I suggest you look either at Apache Commons BeanUtils or Apache Commons Lang, specifically ReflectionToStringBuilder.
Java serialization, which comes with Java, should do the trick. It will be in binary format though.
There is also XML serialization which can be provided by JAXB
you could use reflection
getClass and then go over each instance variable and go on (some objects can be handled specifically (like Strings))
You should use reflection. Have a look at java.lang.Class class, specifically .getFields() method.
I have found the Apache Commons ToStringBuilder.reflectionToString() very useful. To get recursion you can override each Object's toString() method with a call to this function passing this.
http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/builder/ToStringBuilder.html
The java reflection API will give you access to all of this stuff (private members and all). To get private members, you will need to get yourObject.getClass().getDeclaredFields() to access a private field, remember to call yourField.setAccesible(true) on it.
Of course, you are very quickly going to run into problems by handrolling your own class to do this via reflection. The main problems come in when trying to decide to finally print a value and determining if it is an enum, a primitive, a primitive array and so on. You can use the Class.isPrimitive method to help figure that step out. To access elements of an array, use the java.lang.reflect.Array class.
The best option, which was posted earlier, is to use the ReflectionToStringBuilder of apache commons.
A json serializer will do the job, e.g. using Gson:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
...
private static final Logger LOG = LoggerFactory.getLogger(Your.class);
...
Object obj = ...;
LOG.info(new Gson().toJson(obj));

Categories

Resources