Java JSON serialization - best practice
Are you tied to this library? Google Gson is very popular. I have myself not used it with Generics but their front page says Gson considers support for Generics very important.
What is the best practice for saving and retrieving a java object?
You’ll want to read the Java Object Serialization Specification, specifically the Compatible Java Type Evolution section and the section immediately following it, Type Changes Affecting Serialization.
Section 1.10 states:
For serializable objects, sufficient information is kept to restore those objects even if a different (but compatible) version of the implementation of the class is present.
As a developer, you are responsible to making sure that changes to your classes do not conflict with earlier serialized versions. It’s not as hard as you might think. Mostly, you need to avoid incompatible changes:
- Do not delete a field. If it is no longer used, deprecate it. (This includes making an instance field a
static
field; static fields are not serialized, so this is equivalent to removing it as far as serialization is concerned.) - Do not change a field’s type.
You can save additional data by adding a void writeObject(ObjectOutputStream)
method to your class, and you can perform additional initialization by adding a void readObject(ObjectInputStream)
method. These are described in detail in the documentation for Serializable. Note that the first line of code in those methods should be stream.defaultWriteObject()
and stream.defaultReadObject()
, respectively.
readObject
is important when you add fields to a class, if you want those fields to be initialized. For instance, if you have a new field which you always want to be non-null:
private List<String> names = new ArrayList<>();
Any older instance which was serialized without that field present will be deserialized with that field completely unset—that is, it will remain null (since all Object fields are null when an object is created, unless explicitly initialized). You can use readObject
to account for this:
private void readObject(ObjectInputStream stream)
throws IOException,
ClassNotFoundException {
// First, do default serialization
stream.defaultReadObject();
if (this.names == null) {
this.names = new ArrayList<>();
}
}
Best practices for sending/serializing an object
If you use a standard format (JSON, XML, or even proto buffers), there will be far more opportunities for extending your app via integration points. But if it's just internal, then do what's easy. Personally, I create a dedicated persistent proxy class that represents the serialized form of a given object. Then serialize that object using whatever method makes the most sense (Java serialization for over-the-wire live transfers, xml for long term persistence) using writeReplace and readResolve. As the class evolves, I can create completely new implementations of the persistent proxy, add versioning to the proxy, etc... as appropriate. I believe that Bloch discusses this pattern in Effective Java.
As for coming up with a purely from-scratch wire protocol, it's really dependent on how important the performance is to an app. As with most things, the more you can leverage standard libraries/protocols, the faster you can get new code out the door. When I see a huge chunk of code involved with serializing/etc... I generally consider it a code smell and pay very close attention to whether it was justified or not. Just my $0.02.
And PS - someone posted a question about graphs... This is actually one area where I have intentionally avoided standard serialization. Java's ability to serialize complex graphs is not great - you wind up with stack overflow problems (hah) if the graph is even remotely complex. In these cases, a persistent proxy is very, very important.
Java JSON serialization and JSONObject
If this is how you want the class to be serialized
{
"name": "value",
"mykey": "myvalue"
}
Then this is how your object should look like
class Data {
String name, String mykey;
// getters, setters...
}
Alternatively, when @Thomas said, a HashMap, he did not mean "nest" a HashMap into the Object, he literally meant use a HashMap, though, not all JSON libraries support that constructor.
HashMap<String, String> data = new HashMap<String, String>();
data.put("name", "value");
data.put("mykey", "myvalue");
JSONObject json = new JSONObject(data);
String jsonString = json.toString();
Another thing you could do is just treat your object as a JSONObject itself.
class Data extends JSONObject {
public Data() { }
}
Data d = new Data();
d.put("name", "value");
Though, that seems silly.
ObjectMapper - Best practice for thread-safety and performance
private static final ObjectMapper jsonMapper = new ObjectMapper();
Constructing an ObjectMapper
instance is a relatively expensive operation, so it's recommended to create one object and reuse it. You did it right making it final
.
// Suggestion 1:
public static <T> T toObject1(final Class<T> type, final String json) throws IOException {
return jsonMapper.readValue(json, type);
}
You always read JSON to a POJO, so let's be precise and clear, and use ObjectReader
.
// Suggestion 2:
public static <T> T toObject2(final Class<T> type, final String json) throws IOException {
return jsonMapper.readerFor(type).readValue(json);
}
// Suggestion 3:
public static <T> T toObject3(final Class<T> type, final String json) throws IOException {
return jsonReader.forType(type).readValue(json);
}
There is no difference, really. Both methods will construct a new ObjectReader
object: the former (jsonMapper.readerFor(type)
) will give you a fully-built instance directly, the latter (jsonReader.forType(type)
) will complement the not-yet-usable jsonReader
and returns a ready-to-use object. I would rather go with option 2 because I don't want to keep that field.
You shouldn't worry about performance or thread-safety. Even though creating an ObjectMapper
might be costly (or making a copy out of it), getting and working with ObjectReader
s is lightweight and completely thread-safe.
From the Java documentation (emphasis mine):
Uses "mutant factory" pattern so that instances are immutable (and thus fully thread-safe with no external synchronization); new instances are constructed for different configurations. Instances are initially constructed by
ObjectMapper
and can be reused, shared, cached; both because of thread-safety and because instances are relatively light-weight.
I recently had these questions myself and decided on ObjectMapper#reader(InjectableValues)
as a factory method. It's very handy particularly when you want to customise an ObjectReader
slightly or, as it was in my case, to adjust a DeserializationContext
.
That's an excellent question, by the way.
What is the accepted practice for custom serialization in jackson?
Long story short: this really comes down to your personal preferences, as well as whether you have access to source code of value objects.
The difference is between in-built (implement JsonSerializable
) or external serializers (annotation or module); as well as internal (annotation) vs external (module) association/registration of serializers.
So I would not say there is universal preference: each works, is supported, and (in my opinion) makes sense for some cases.
Some developers prefer external approach because it allows value classes to have no dependencies to Jackson types: when implementing JsonSerializable
, for example, you add a hard dependency to jackson-databind
. With @JsonSerialize
annotation you get a weaker dependency to jackson-annotations
, whereas with module approach there is no direct dependency at all.
There are potential benefits to implementing JsonSerializable
, in that this allows all details of the value class and its handling (including serialization) to be encapsulated within class definition.
Some would also consider that a downside; it depends on your view on proper OOP practices vs practicality of development.
Now: in some cases, such as when supporting 3rd party libraries, your choices are limited to external module, or use of mix-in annotations.
For your own value types you can pick and choose.
Conditionally serialize collection field best practice?
Well I took Adam Gent advice.
I put @JsonIgnore on all reference fields, implemented several DTO's which are just wrappers of my entity classes, and I response in rest services with these DTO's.
how to apply Json serialization or deserialization in request and response to HTTP method of rest api
You can use Jackson
API to play with JSON.
For the Following JSON data the Java object mapping can be done as follows.
{
"id": 123,
"name": "Pankaj",
"permanent": true,
"address": {
"street": "Albany Dr",
"city": "San Jose",
"zipcode": 95129
},
"phoneNumbers": [
123456,
987654
],
"role": "Manager",
"cities": [
"Los Angeles",
"New York"
],
"properties": {
"age": "29 years",
"salary": "1000 USD"
}
}
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.journaldev.jackson.model.Address;
import com.journaldev.jackson.model.Employee;
public class JacksonObjectMapperExample {
public static void main(String[] args) throws IOException {
//read json file data to String
byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));
//create ObjectMapper instance
ObjectMapper objectMapper = new ObjectMapper();
//convert json string to object
Employee emp = objectMapper.readValue(jsonData, Employee.class);
System.out.println("Employee Object\n"+emp);
//convert Object to json string
Employee emp1 = createEmployee();
//configure Object mapper for pretty print
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
//writing to console, can write to any output stream such as file
StringWriter stringEmp = new StringWriter();
objectMapper.writeValue(stringEmp, emp1);
System.out.println("Employee JSON is\n"+stringEmp);
}
public static Employee createEmployee() {
Employee emp = new Employee();
emp.setId(100);
emp.setName("David");
emp.setPermanent(false);
emp.setPhoneNumbers(new long[] { 123456, 987654 });
emp.setRole("Manager");
Address add = new Address();
add.setCity("Bangalore");
add.setStreet("BTM 1st Stage");
add.setZipcode(560100);
emp.setAddress(add);
List<String> cities = new ArrayList<String>();
cities.add("Los Angeles");
cities.add("New York");
emp.setCities(cities);
Map<String, String> props = new HashMap<String, String>();
props.put("salary", "1000 Rs");
props.put("age", "28 years");
emp.setProperties(props);
return emp;
}
}
Source : http://www.journaldev.com/2324/jackson-json-processing-api-in-java-example-tutorial
Related Topics
Jtable + Sorting Specific Field
Reset Buffer with Bufferedreader in Java
Efficient Intersection of Two List<String> in Java
Ways to Save Enums in Database
Execute Jsp Directly from Java
Calling a Java Method to Draw Graphics
Java: Jackson Polymorphic JSON Deserialization of an Object with an Interface Property
How to Pause/Sleep/Wait in a Java Swing App
What Is the Actual Memory Place for Static Variables
Auto Adjust the Height of Rows in a Jtable