gson exclusion strategy apply only to fields of target object - java

I want to prevent Gson from serializing fields of a specific type. For this, I have created an exclusion strategy. The exclusion strategy does successfully recognize when the class in question is being processed and it does successfully exclude it. Unfortunately, it prevents me from serializing objects of that class even when they are the root. By that I mean they are the argument passed to the gson.toJson() method.
To be more clear, I have a class of type Person with class fields that themselves involve the Person type. I do not want to serialize class fields of the type Person.
public class Person{
private Person child;
private String name;
}
So, in the above example, I want a json object containing the name field but not the child field. I want the solution to be sensitive the the type of the field, not the field name.

An ExclusionStrategy defines two methods, one to exclude types and one to exclude fields. Just use the field method to skip any fields of type Person.
class PersonExcluder implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredType().equals(Person.class);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
then use register it
Gson gson = new GsonBuilder().setExclusionStrategies(new PersonExcluder()).create();

Related

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.

Spring, JSON serializer, serialize a field only in some cases

I have a class Order:
#Data
#Entity
public class Order {
private List<Project> projects;
// more fields
}
I have a two API methods in my controller:
#GetMapping
public ResponseEntity<List<Order>> getOrders() {
return ResponseEntity.ok(orderService.getOrders());
}
#GetMapping("/{id}")
public ResponseEntity<Order> getOrder(#PathVariable long id) {
return ResponseEntity.ok(orderService.getOrder(id));
}
So in this case projects is always sent via JSON, if its present its just getting serialized, if its not present its getting fetched lazily and then serialized. I could avoid it being serialized by annotating the field with #JsonIgnore. But the problem is that i want to send it sometimes and sometimes i dont. For example in getOrders() i dont want the projects to be serialized. In getOrder(...) i would want projects to be serialized. Is there any way to tell during runtime either inside custom code or by an annotation that i want to send it in one specific case and not in another case? The only thing i figured out is that - shortly before serializing - i can initialize projects with null and annotate the entity with #JsonInclude(JsonInclude.Include.NON_NULL). That way it wouldnt be sent and if i want to send it i can just avoid initializing it with null. But obviously i dont want to iterate over each Order in O(n) just to initialize its projects with null.
This is easy to achieve using "JSON Views".
First, define some classes to represent each view (e.g. internal/external):
public class OrderViews {
public static class OnlySomeFields {}
public static class AllFields extends OnlySomeFields {}
}
Next, on your class, assign a view to each field:
public class Order {
#JsonView(OrderViews.OnlySomeFields.class)
private String foo;
#JsonView(OrderViews.AllFields.class)
private String bar;
// getters/setters/etc
}
Then, in your controller, you can specify which view to use for each method:
#RestController
public class MyController {
#JsonView(OrderViews.AllFields.class)
#GetMapping("/with-all-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
#JsonView(OrderViews.OnlySomeFields.class)
#GetMapping("/with-some-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
}
With this setup, navigating to /with-all-fields returns a JSON containing foo and bar, while navigating to /with-some-fields returns a JSON only containing foo.
You can use this technique to selectively serialize specific fields, and should be able to apply it to your use case.

Java Annotation Processing how to check if VariableElement is a primitive type(int, float) or an object some class

I have a class
public class SomeClass {
#CustomAnnotation1
String stringProperty = "LALALA";
#CustomAnnotation1
int integerProperty;
#CustomAnnotation1
ClassObject classObject;
}
CustomAnnotation1 is a custom annotation defined by me which can be put over any Field. Suppose class ClassObject is something like
public class ClassObject {
#CustomAnnotation1
public String someOtherString;
public String log;
}
What I want to achieve - If my annotation is put on any field which is not a primitive type, I want to access all the fields of that class.
My Approach - Get all the fields annotated with CustomAnnotation1, iterate over all of them and if it is non-primitive, get all the fields inside that class and process.
What I've tried - I am able to get all the elements annotated with my annotation using the below code in my AbstractProcessor class.
Collection<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(CustomAnnotation1.class);
List<VariableElement> variableElements = ElementFilter.fieldsIn(annotatedElements);
Questions -
I've researched a lot about the VariableElement class but unable to find a way to check if the field is primitive or not. Can this be done?
Is there any better approach to achieve this?
VariableElement.asType().getKind().isPrimitive()

Jackson deserialisation mappings to Java Generics

What I am trying to do is the following: Given a JSON document, map it to a POJO using Jackson, but define the type of the Generic class member based on a field in the JSON document.
My JSON looks as follows
{
"name": "Name",
"parameters": [
{"name": "paramName","value": "Value1", "#type": "string"},
{"name": "size","value": 5,"#type": "double"}
]
}
The class that maps to this JSON doc is
public class Strategy {
public String name;
public List<Parameter<?>> parameters;
}
Then I have a Generic class for this as follows
public class Parameter<T> {
public String name;
public T value;
#Override
public String toString() {
return this.getClass().getName();
}
}
So the idea is to tell Jackson when you deserialize the JSON document into the Strategy class and get to the parameters field, use the following classes as the Generic data type for the value member, i.e. I want to select it to be String or Double or Integer but I want that to be my decision so that it's generic and can be extended to any data type I want.
I realise I can use the annotation JsonTypeInfo which I added as well like this
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
But using these classes as is actually works but Jackson decides itself what the type should be based on its value and my size parameter is set to an Integer. If I set its value to 5.0 then its set to a Double which works, but what if I want one of the parameters to be a custom object?
The only way I could get this to work (and am not 100% happy with the solution) is to make the Parameter class abstract and then create concrete classes for each type that I want, i.e. ParameterString, ParameterDouble, ParameterCustomClass and then use the #JsonSubTypes annotations to set the correct class to use based on the type field in the JSON document.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
#JsonSubTypes({
#JsonSubTypes.Type(value=ParameterString.class, name="string"),
#JsonSubTypes.Type(value=ParameterDouble.class, name="double"),
#JsonSubTypes.Type(value=ParameterInstrument.class, name="instrument")
})
With the following class as an example
public class StrategyParameterString extends StrategyParameter<String> {
}
This isn't very extendable, I guess it will just need a new subtype annotation and concrete class added for every type that I need, but just doesn't feel as elegant as it could be.
Does anyone know of a better way of handling this ?
Thanks
Andrew
As I understand it, the types you want to represent in your Parameter list are reifiable, eg. String, Double, Instrument. You can take advantage of the fact that reifiable types have a runtime type token in the form of their class literal. This can be exploited to form the basis of a heterogenous type safe collection.
Instead of defining your Parameter class this way:
public class Parameter<T> {
public String name;
public T value;
:
:
}
}
You can define it as a concrete class that associates the object's value with its run time type token.
public class Parameter() {
private final Object m_value;
private final Class<?> m_cls;
private Parameter(Class<?> token, Object val) {
m_value = val;
m_cls = token;
}
public static <T> Parameter newInstance(Class<T> token, T value) {
return new Parameter(token, value);
}
:
:
public <T> T getValue(Class<T> token) {
if (token != m_cls) throw new ClassCastException("Type error");
return token.cast(m_value);
}
}
In this setting, type tokens and generic methods (rather than a generic type) are used to set and reestablish the type linkage for the desired value. The value you set can be any type and is guaranteed to be returned as the same type that you stored as long as the type tokens are consistent.
Note that constructors can not be generic. To address this, the constructor for Parameter has been made private and Parameter instances are formed by invoking the newInstance() static factory method (which can be generic).

Gson add field during serialization

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.

Categories