Gson add field during serialization - java

I can't find a simple way to add a custom field during serialization in Gson and I was hoping someone else may be able to help.
Here is a sample class to show my issue:
public class A {
String id;
String name;
...
}
When I serialize class A I would like to return something like:
{ "id":"123", "name":"John Doe", "url_to_user":"http://www.example.com/123" }
where url_to_user is not stored in my instance of class A, but can be generated with data in the instance of class A.
Is there a simple way of doing this? I would prefer to avoid writing an entire serializer just to add one field.

Use Gson.toJsonTree to get a JsonElement, with which you can interact dynamically.
A a = getYourAInstanceHere();
Gson gson = new Gson();
JsonElement jsonElement = gson.toJsonTree(a);
jsonElement.getAsJsonObject().addProperty("url_to_user", url);
return gson.toJson(jsonElement);

Well, the top rated answer is quite a quick one and not essentially bad when you are lacking much time but here is the problem: There is no proper separation of concern
You are modifying the serialized JSON at the same place where you are writing your business logic. You should be doing all the serialization inside of a TypeAdapter or a JsonSerializer.
How can we maintain a proper separation of concern?
The answer wraps around a bit of additional complexity but the architecture demands it. Here we go(taken from my other answer):
First, we would be using a custom serializer for the type. Second, we would have to create a copy constructor inside the base class and a wrapper subclass as follows:
Note: The custom serializer might seem like an overkill but trust me, it pays off in long run for maintainability.
.
// Lets say the base class is named Cat
public class Cat {
public String name;
public Cat(String name) {
super();
this.name = name;
}
// COPY CONSTRUCTOR
public Cat(Cat cat) {
this.name = cat.name;
}
#Override
public String sound() {
return name + " : \"meaow\"";
};
}
// The wrapper subclass for serialization
public class CatWrapper extends Cat{
public CatWrapper(String name) {
super(name);
}
public CatWrapper(Cat cat) {
super(cat);
}
}
And the serializer for the type Cat:
public class CatSerializer implements JsonSerializer<Cat> {
#Override
public JsonElement serialize(Cat src, Type typeOfSrc, JsonSerializationContext context) {
// Essentially the same as the type Cat
JsonElement catWrapped = context.serialize(new CatWrapper(src));
// Here, we can customize the generated JSON from the wrapper as we want.
// We can add a field, remove a field, etc.
// The main logic from the top rated answer now here instead of *spilling* around(Kindly ignore the cat having a url for the sake of example)
return catWrapped.getAsJsonObject().addProperty("url_to_user", url);
}
}
So, why a copy constructor?
Well, once you define the copy constructor, no matter how much the base class changes, your wrapper will continue with the same role. Secondly, if we don't define a copy constructor and simply subclass the base class then we would have to "talk" in terms of the extended class, i.e, CatWrapper. It is quite possible that your components talk in terms of the base class and not the wrapper type.

Related

Write generic static method in enum base class that can extract subclass instance to do a Jackson conversion

I have some enum types that look like this:
public static enum Thingie {
ABC("abc"), DEF("def");
private String messageValue;
#JsonValue
public String getMessageValue() { return messageValue; }
private Thingie(String messageValue) { this.messageValue = messageValue; }
}
This will allow Jackson to properly marshal and unmarshal between string values and the enum type.
There may be times when I'd like to directly convert a string value to the enum value. This would be like the internal "fromValue()" method, but not quite the same:
public static Thingie messageValueOf(String messageValue) {
ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(messageValue, Thingie.class);
}
I would like to convert this into a generic method AND put it into a base class, along with the "messageValue" property and accessor. The constructor would change to just call "super(messageValue)". Obviously, if I could do that, I would move the "mapper" to class level.
At this point, I've only attempted to write this as a generic method in the single enum type. I can't even get that working. I can't figure out how to extract the class from the template parameter. I've seen this particular question before, and there have been some answers, but I couldn't quite get it to work, and I imagine trying to do this in the base class would add additional complexity.
Let's assume I understood your problem (correct me if I am wrong).
The constructor would change to just call "super(messageValue)"
An enum can not extend a class, so you can't do that. But you can create an interface/class which you will delegate to for such queries (very simplistic code):
interface Test {
ObjectMapper MAPPER = new ObjectMapper();
static <T extends Enum<T>> T getIt(String s, Class<T> clazz) {
return MAPPER.convertValue(s, clazz);
}
}
public static void main(String[] args) {
Thingie abc = Test.getIt("abc", Thingie.class);
System.out.println(abc.ordinal());
}

Jackson: passing exta objects during deserialization

During deserialization, how can I pass in an extra object that's needed to initialize some class member? If I were doing deserialization "manually," the implementation might look like:
public class MyClass {
private MyDocumentObject do;
private String food;
public MyClass(JsonNode node, MyDocument document) {
this.do = document.createMyDocumentObject();
this.food = node.get("food").asText();
}
public String getFood() {
return this.food;
}
}
But I'd like to use Jackson's automatic mapping facilities and use a decorated constructor or custom deserializer, etc. and avoid implementing the deserialization within the class itself. Looking at example implementations using #JsonCreator or extending StdDeserializer, I can't see a way of saying "hey, please use this MyDocument object when you call the constructor." I'd like to avoid implementing and exposing a separate method that accepts a MyDocument that I have to invoke on every object that gets deserialized, e.g.
public createDocumentObject(MyDocument document) {
this.do = document.createMyDocumentObject();
}
I don't want to have this method at all, but if I had to, I'd want Jackson to call this method for me right after deserialization. That means I'd still have to somehow tell Jackson which MyDocument to use.

How to implement a Gson equivalent of #JsonUnwrap

I know Gson doesn't come with a similar feature, but is there a way to add support for unwrapping Json fields the way #JsonUnwrap does?
The goal is to allow a structure like:
public class Person {
public int age;
public Name name;
}
public class Name {
public String first;
public String last;
}
to be (de)serialized as:
{
"age" : 18,
"first" : "Joey",
"last" : "Sixpack"
}
instead of:
{
"age" : 18,
"name" : {
"first" : "Joey",
"last" : "Sixpack"
}
}
I understand it could get fairly complex, so I'm not looking for a full solution, just some high-level guidelines if this is even doable.
I've made a crude implementation of a deserializer that supports this. It is fully generic (type-independent), but also expensive and fragile and I will not be using it for anything serious. I am posting only to show to others what I've got, if they end up needing to do something similar.
public class UnwrappingDeserializer implements JsonDeserializer<Object> {
//This Gson needs to be identical to the global one, sans this deserializer to prevent infinite recursion
private Gson delegate;
public UnwrappingDeserializer(Gson delegate) {
this.delegate = delegate;
}
#Override
public Object deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
Object def = delegate.fromJson(json, type); //Gson doesn't care about unknown fields
Class raw = GenericTypeReflector.erase(type);
Set<Field> unwrappedFields = ClassUtils.getAnnotatedFields(raw, GsonUnwrap.class);
for (Field field : unwrappedFields) {
AnnotatedType fieldType = GenericTypeReflector.getExactFieldType(field, type);
field.setAccessible(true);
try {
Object fieldValue = deserialize(json, fieldType.getType(), context);
field.set(def, fieldValue);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return def;
}
}
It can then be registered globally via new GsonBuilder().registerTypeHierarchyAdapter(Object.class, new UnwrappingDeserializer(new Gson())).create() or for a specific type via registerTypeAdapter.
Notes:
A real implementation should recursively check the entire class structure for the presence of GsonUnwrap, cache the result in a concurrent map, and only go through this procedure if it needs to. Otherwise it should just return def immediately
It should also cache discovered annotated fields to avoid scanning the hierarchy each time
GenericTypeReflector is coming from GeAnTyRef
ClassUtils#getAnnotatedFields is my own implementation, but it doesn't do anything special - it just gathers declared fields (via Class#getDeclaredFields) recursively for the class hierarchy
GsonUnwrap is just a simple custom annotation
I presume a similar thing can be done for serialization as well. Examples linked from Derlin's answer can be a starting point.
Currently, there is no easy way to do that. Here are anyway some pointers/alternative ways to make it work.
GsonFire: GsonFire implements some useful features missing from Gson. While it does not yet offer automatic wrapping/unwrapping, it may be a good starting point to create your custom logic.
If you only need serialization, you can add getters for first and last in Person and use #ExposeMethodResult to serialize them. Unfortunately, setters are not supported (cf. Is possible to use setters when Gson deserializes a JSON?).
Another way to support the serialization is to follow the advices from How to move fields to parent object.
Custom TypeAdapters : on of the only ways to support both serialization and deserialization is to create custom TypeAdapters. This won't be generic, but it will suit your usecase.
The thread Serialize Nested Object as Attributes already gives you examples, so I won't repeat them here.

Passing POJO class, object to another POJO

I am sure this question might have been asked couple of times here but I am not understanding what query I should use.
What I want to do is, Passing POJO to another POJO where parameter could be dynamic
like example below
Class DataPOJO{
private String name;
public String getName(){
return this.name;
}
public void setName(String name){
return this.name;
}
}
I want to make another POJO where I can pass value like
RequestmakerPOJO request = new RequestmakerPOJO(authorisationObject, dataPOJO, DataPOJO);
Where dataPOJO would be object I created and DataPOJO.class it's structure, later I can pass any kind of pojo to this new Requestmaker class with it's structure definition and object just like. HashMap<ObjectType, ObjectType2>
But I want only 1 Object type to be passed and then it's object. I am new at java so I don't know what to call these scenarios. Please help me out with the query or solution.
:) thanks
You should declare RequestmakerPOJO as a generic class:
class RequestmakerPOJO<T> {
...
}
Now you can use T as a type wherever you want. For example, the constructor can be
RequestmakerPOJO(AuthPOJO auth, T data) {
...
}
To declare a variable
RequestmakerPOJO<DataPOJO> request = new RequestmakerPOJO<>(auth, data);
For more details, research generics in Java.

Implementing Deserialization of Objects

As usual, I'm sure there is a multitude of answers out there to this question, however, I have no idea what to search to find them.
Say you have a bunch of animals, Rat Cat Dog and Fish, and you want to serialize/deserialize these animals. Each has its own serialize() method. That all works fine and good, if you want to save your Cat to a file, you call Cat.serialize() and then you write it to a file.
But what about deserializing? Sure, you could save an extra bit of data that states the type of animal, then use reflection to get a static method (say, deserialize(String serialized)) and then invoke it. But that's not safe. What if someone creates a new animal 'Rabbit' and doesn't include a deserialize(String serialized) method? Sure, you could then just catch the error and ignore the issue, but that seems unclean and bad practice.
You could also create an enum. Each item in the enum must have a deserialize(String serialized) method, and the aforementioned piece of data that states the type references the name of its enum item. Very clean. Problem is, not very adaptable. Enums don't allow for other animals to be added at runtime.
The way I have been solving this issue is mapping the name of the objects against a deserializer object. Basically each animal would have to 'register' itself by entering it's name and deserializer (implements Deserializer interface) object into a HashMap. The deserializer object can then be retrieved via the name of the animal at a later time.
My question is, what is the best method to go about implementing deserialization? Also, is the method I have been using good/bad practice?
Finally, if you are wondering why animals would be added at runtime, the reason is because animals might be added by other non accessible parts of the program, or by things such as plugins.
Example of the method I have been using is below.
Animal Interface
public class Animal{
public String serialize();
}
Deserializer Interface
public interface Deserializer{
public Animal deserialize();
}
Deserializer Storer:
public class AnimalSpecies{
public static final String DELIMITER="|";
private static HashMap<String,GameModeType> species=new HashMap<String,GameModeType>();
public static addSpecies(String speciesName,Deserializer deserializer){
if (species.contains(speciesName)){
//Throw error
}
species.put(speciesName,deserializer);
}
public static Deserializer getSpeciesDeserializer(String speciesName){
return species.get(speciesName);
}
}
Cat class
public class Cat implements Animal{
public static final String DELIMITER=AnimalSpecies.DELIMITER;
private String type;
private int age;
private String name;
public static register(){
AnimalSpecies.addSpecies("CAT",new deserializer());
}
public Cat(String name,String type,Int age){
this.type=type;
this.age=age;
this.name=name;
}
public String serialize(){
return "CAT"+DELIMITER+type+DELIMITER+age+DELIMITER+name;
}
private static class deserializer implements Deserializer{
#Override
public Cat deserialize(String serialized){
String[] split=serialized.split(DELIMITER);
return new Cat(split[3].split[1],Integer.parseInt(split[2]));
}
}
}
Then in you main method you would call:
Cat.register();
And when you needed to deserialize:
Deserializer d=AnimalSpecies.getSpeciesDeserializer(serialized.split(AnimalSpecies.DELIMITER)[0]);
if (d!=null){
d.deserialize(serialized);
}
After writing all this out, I cant help but notice how much my method requires cooperation/understanding from other parties. It cant be guaranteed that the correct delimiter will be used. What I mean by this is that any serialized Animal must have a way of identifying which animal it is so that it's deserializer can be accessed. This can be worked around by implementing a wrapper that adds this information directly before it is written to a file, and removes it directly before deserialization.
Why not use built in serialization/deserialization?
I would like the serialized data to be readable and easily editable.

Categories