Java JSON Serialization - Best Practice

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 ObjectReaders 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



Leave a reply



Submit