Programmatically accessing #JsonProperty from Java - java

I have the following POJO using Immutables+Jackson under the hood:
#JsonInclude(JsonInclude.Include.NON_NULL)
abstract class AbstractQueryRequest {
#JsonProperty("reqid")
public abstract String reqid();
#JsonProperty("rawquery")
public abstract String rawquery();
}
At some point I need to build another object based on the fields of the POJO, something along this line:
final HttpUrl.Builder urlBuilder = HttpUrl.parse(cfg.baseUrl()).newBuilder();
urlBuilder.addQueryParameter("reqid", request.reqid())
.addQueryParameter("rawquery", request.rawquery());
It's quite annoying to keep the POJO and this call aligned upon changes, I was wondering if it was possible to access programmatically each JsonProperty instead of typing the string manually.
Note that it is fine to write the getters by hand as I can easily refactor and I have the compiler double checking, but for strings I am worried for people down the line and I would like to "read" them from the POJO class somehow.

You can do it via reflection. You need to take method annotation values which annotated with JsonProperty. But I recommend you to use JsonProperty on fields, not methods.
Here is an example for your current requirement :
public class Main {
public static void main(String[] args) {
AbstractQueryRequest someType = new SomeType();
for(Method method : x.getClass().getSuperclass().getDeclaredMethods()) {
if (method.isAnnotationPresent(JsonProperty.class)) {
JsonProperty annotation = method.getAnnotation(JsonProperty.class);
System.out.println(annotation.value());
}
}
}
}
class SomeType extends AbstractQueryRequest {
#Override
public String reqid() {
return null;
}
#Override
public String rawquery() {
return null;
}
}
Output is :
rawquery
reqid

Related

HashMap in HashMap Java [duplicate]

I have a structure with circular references.
And for debug purposes, I want to dump it. Basically as any format, but I chose JSON.
Since it can be any class, I chose GSON which doesn't needs JAXB annotations.
But GSON hits the circular references and recurses until StackOverflowError.
How can I limit GSON to
ignore certain class members?
Both #XmlTransient and #JsonIgnore are not obeyed.
ignore certain object graph paths? E.g. I could instruct GSON not to serialize release.customFields.product.
go to the depth of at most 2 levels?
Related: Gson.toJson gives StackOverFlowError, how to get proper json in this case? (public static class)
Simply make the fields transient (as in private transient int field = 4;). GSON understands that.
Edit
No need for a built-in annotation; Gson lets you plug in your own strategies for excluding fields and classes. They cannot be based on a path or nesting level, but annotations and names are fine.
If I wanted to skip fields that are named "lastName" on class "my.model.Person", I could write an exclusion strategy like this:
class MyExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes fa) {
String className = fa.getDeclaringClass().getName();
String fieldName = fa.getName();
return
className.equals("my.model.Person")
&& fieldName.equals("lastName");
}
#Override
public boolean shouldSkipClass(Class<?> type) {
// never skips any class
return false;
}
}
I could also make my own annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface GsonRepellent {
}
And rewrite the shouldSkipField method as:
public boolean shouldSkipField(FieldAttributes fa) {
return fa.getAnnotation(GsonRepellent.class) != null;
}
This would enable me to do things like:
public class Person {
#GsonRepellent
private String lastName = "Troscianko";
// ...
To use a custom ExclusionStrategy, build Gson object using the builder:
Gson g = new GsonBuilder()
.setExclusionStrategies(new MyOwnExclusionStrategy())
.create();
I know this question has a few years now, but I'd like to contribute with my solution.
Although #fdreger's answer is completely valid in case you want to exclude a field always, it doesn't work if you want to exclude it just in certain cases, avoiding this way the recursion.
The way I approached the problem is:
I write my own JsonSerializer. In it, I define a static variable to control de number of times an object of this same class is serialize and depending on the value, the object can be serialized or not.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class UserJsonSerializer extends JsonSerializer<User> {
private static final ThreadLocal<Integer> depth = new ThreadLocal<Integer>() {
#Override
protected Integer initialValue() {
return 0;
}
};
#Override
public void serialize(User user, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
// Here, we limit the number of instances to return. In this case, just 1.
depth.set(depth.get() + 1);
if(depth.get() >= 1) {
generator.writeNull();
} else {
generator.writeObject(user);
}
}
public static void clear() {
depth.remove();
}
}
Bind the UserJsonSerializer to the class you want to control
public class SomeClass implements Serializable {
#JsonSerialize(using = UserJsonSerializer.class)
private User user;
//...others fields...
}
Don't forget to call UserJsonSerializer#clear() method to reinitialize the counter everytime you're going to parse a new entity.
I hope this helps.

Rename field only on Serialization in java

I have an object named AddOnsSRO.Only on serialization I want the names of fields of the object to be changed.
Tried using #JsonProperty on getter methods but it gives me a renamed field even on usages where serialization is not involved.
public class AddOnsSRO {
private String sideCar;
private String sideCarCoverage;
#JsonSerialize
#JsonProperty("abc")
public String getSideCar() {
return sideCar;
}
public void setSideCar(String sideCar) {
this.sideCar = sideCar;
}
#JsonSerialize
#JsonProperty("xyz")
public String getSideCarCoverage() {
return sideCarCoverage;
}
public void setSideCarCoverage(String sideCarCoverage) {
this.sideCarCoverage = sideCarCoverage;
}
}
Only on serialization the following fields : sideCar and sideCarCoverage must be renamed to abc and xyz respectively.
For any other use except serialization the field names should be sideCar and sideCarCoverage only.
Please help and suggest changes or annotations accordingly.
For effecting only serializing use #JsonGetter instead of #JsonProperty
#JsonGetter("abc")
public String getSideCar() {
return sideCar;
}
Getter means that when serializing Object instance of class that has this method (possibly inherited from a super class), a call is made through the method, and return value will be serialized as value of the property.
You can add #JsonSetter to setter method for deserialize:
#JsonSetter("sideCar")
public void setSideCar(String sideCar) {
this.sideCar = sideCar;
}
your code looks good...Please upgrade your jackson lib... if you are using old

How to add derived properties to a Jackson 2 serialized class?

I'm serializing some existing objects with Jackson 2.22, leveragin the MixIn feature to decouple the real object from the Jackson annotations configuration.
Actually my mixin is an interface that declares the same methods of the target class and annotates them, here's an example.
Target class:
public class Product {
// ...
public String getName();
public String getDescription();
public String getPrice();
public String getFinalPrice();
public String getDiscount();
// ...
}
and the mixin:
public interface ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
}
My JSON should have some more informations, computed from several methods or fields of the target class.
Is this even possible in Jackson?
I tried turning the mixin in a class and adding a new method there, but that didn't work.
public class ProductApi {
#JsonProperty
public String getName();
#JsonProperty("price")
public String getFinalPrice();
#JsonProperty("images")
public List<String> getImages() { /* ... */ }
}
I guess this is because the mixin only provides annotations for the target class, but is the latter that is read for serialization.
Of course, if I change the object to be serialized with a new subclass that contains the new method I need, that works, but the objects come from our services layers, and this would mean I have to rewrite all those methods.
I'm using Jackson with Jersey, so don't want to change Jackson with another library.
Here's how I did it.
The solution is to specify a custom JsonSerializer implementation to the field getter.
First of all, I changed the mixin interface to a class that extends the entity (target) class, so that it can access the target class data.
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
}
Next, I implemented the JsonSerializer that would create the derived property I want:
public static class ImagesSerializer extends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Product p = (Product) jgen.getCurrentValue();
int num = p.getNumberOfImages();
List<String> imgs = new ArrayList<String>(num);
for(int i = 0; i < num; i++) {
String src = "/include/images/showImage.jsp?"+"id="+p.getId()+"&number="+i;
imgs.add(src);
}
provider.defaultSerializeValue(imgs, jgen);
}
}
This is a really simple implementation, more safety checks should be done.
What this does is, basically, retrieve the whole entity instance from the JSON generator, build up a custom object and then ask Jackson to serialize it.
I implemented it inside my ProductApi as a static class, but just for simplicity.
Finally, the serializer needs to be bound to the JsonProperty annotated field:
public class ProductApi extends Product {
#JsonProperty
#Override
public String getName() {
return super.getName();
};
// ...
#JsonSerialize(using=ImagesSerializer.class)
#JsonProperty("images")
#Override
public String getImage() { // in my entity this returns an image number, whereas in my JSON I want a list of URLs
return "";
}
// ...
}
As a side note, it seems that the returned value of the getImage() method is not used.
Why don't you just make some fields, which should be serialized and use Gson for it?

Jackson serialization: how to ignore superclass properties

I want to serialize a POJO class which is not under my control, but want to avoid serializing any of the properties which are coming from the superclass, and not from the final class. Example:
public class MyGeneratedRecord extends org.jooq.impl.UpdatableRecordImpl<...>,
example.generated.tables.interfaces.IMyGenerated {
public void setField1(...);
public Integer getField1();
public void setField2(...);
public Integer getField2();
...
}
You can guess from the example that that this class is generated by JOOQ, and inherits from a complex base class UpdatableRecordImpl which also has some bean property-like methods, which cause problems during the serialization. Also, I have several similar classes, so it would be good to avoid duplicating the same solution for all of my generated POJOs.
I have found the following possible solutions so far:
ignore the specific fields coming from superclass using mixin technique like this: How can I tell jackson to ignore a property for which I don't have control over the source code?
The problem with this is that if the base class changes (e.g., a new getAnything() method appears in it), it can break my implementation.
implement a custom serializer and handle the issue there. This seems a bit overkill to me.
as incidentally I have an interface which describes exactly the properties I want to serialize, maybe I can mixin a #JsonSerialize(as=IMyGenerated.class) annotation...? Can I use this for my purpose?
But, from pure design point of view, the best would be to be able to tell jackson that I want to serialize only the final class' properties, and ignore all the inherited ones. Is there a way to do that?
Thanks in advance.
You can register a custom Jackson annotation intropector which would ignore all the properties that come from the certain super type. Here is an example:
public class JacksonIgnoreInherited {
public static class Base {
public final String field1;
public Base(final String field1) {
this.field1 = field1;
}
}
public static class Bean extends Base {
public final String field2;
public Bean(final String field1, final String field2) {
super(field1);
this.field2 = field2;
}
}
private static class IgnoreInheritedIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return m.getDeclaringClass() == Base.class || super.hasIgnoreMarker(m);
}
}
public static void main(String[] args) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new IgnoreInheritedIntrospector());
final Bean bean = new Bean("a", "b");
System.out.println(mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(bean));
}
}
Output:
{
"field2" : "b"
}
You can override the superclass' methods which you'd like to prevent from being output and annotate them with #JsonIgnore. The override shifts the control of property creation to the subclass while enabling its ability to filter it from the output.
For instance:
public class SomeClass {
public void setField1(...);
public Integer getField1();
public void setField2(...);
public Integer getField2();
#Override
#JsonIgnore
public String superClassField1(...){
return super.superClassField1();
};
#Override
#JsonIgnore
public String superClassField2(...){
return super.superClassField2();
};
...
}
You can use this as well instead of unnecessary overrides
#JsonIgnoreProperties({ "aFieldFromSuperClass"})
public class Child extends Base {
private String id;
private String name;
private String category;
}
The good use of inheritance is that the child classes extend or add functionality. So the usual way is to serialize the data.
A workarround would be to use a Value Object (VO) or Data Transfer Object (DTO) with the fields you need to serialize. Steps:
Create a VO class with the fields that should be serialized.
Use BeanUtils.copyProperties(target VO, source data) to copy the properties
Serialize the VO instance.
Add the following annotation in your Base Class :
#JsonInclude(Include.NON_NULL)

How to get all accessor methods for an attribute in a class?

I want to retrieve all accessors for a given attribute in a Java class. I've tried a few things but it is not giving the output I am expecting. For eg:
public class demo {
private String abc;
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
public String fetchAbc() {
return abc;
}
}
Here, the abc attribute has two getters, and I want to find there occurences in my project. I tried the following code, which uses the BeanInfo API, but it gives me only one accessor:
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class BeanDemo {
public void myMethod() throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(demo.class);
for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
//all attributes of class.
property.getReadMethod(); // getter
property.getWriteMethod(); // setter
}
}
}
Can anyone tell if there is another API that can be used to accomplish this? I can play with reflection but it is not proper way. Thanks.
First: following JavaBean conventions is helpful. If you have a method that says "retrieve" yet it is an actual getter, it's more confusing to yourself than it is Java - why should there be more than one way to get a field?
Second: reflection is your friend. You can reflectively pull information about an object with very little pain, and retrieve the results you want. You should look at the Reflection Trail, as it contains a ton of useful information to get you started.
Here's a minor sample - it will get all of the methods in this class labeled "get".
public class StackOverflow {
public static void main(String[] args) throws ClassNotFoundException {
Method[] methods = Class.forName("Fake").getMethods();
for(Method m : methods) {
if(!m.getDeclaringClass().equals(Object.class) && m.toGenericString().contains("get")) {
System.out.println(m.toGenericString());
}
}
}
}
class Fake {
private String objField;
private int primitiveField;
private boolean[] arrayField;
public void setObjField(final String theObjField) {
objField = theObjField;
}
public void setPrimitiveField(final int thePrimitiveField) {
primitiveField = thePrimitiveField;
}
public void setArrayField(final boolean[] theArrayField) {
arrayField = theArrayField;
}
public String getObjField() {
return objField;
}
public int getPrimitiveField() {
return primitiveField;
}
public boolean[] getArrayField() {
return arrayField;
}
}
The above outputs:
public java.lang.String Fake.getObjField()
public int Fake.getPrimitiveField()
public boolean[] Fake.getArrayField()
I would encourage you to "play with reflection" as it is exactly the proper way.
Start here, for reflection in general, or here, for classes. You may also find this reference page useful.
Here's a hint:
Method[] methods = demo.class.getMethods();
for (int i=0; i<methods.length; i++) {
Method method = methods[i];
// Take a look at the Method documentation to find out more:
// http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html
// Or, look at the Method tutorial here:
// http://docs.oracle.com/javase/tutorial/reflect/member/method.html
}
Check out the Class<T> class and the Method class at the very least.
Reflection is going only to look at the names and signatures of the methods, it does not look at what they actually do. So can you reliably identify an get/set on that basis?
You can identify all fields: aaa, bbb etc, and then look for all methods whose signatures look like accessors and whose names contain aaa or bbb etc. So you'd find getAaa(), setBbb() and fetchAaa(), and perhaps even bbbFetcher(), but you cannot be sure that any of these really are what you are looking for. Even were you to scan the source it's tricky: Consider
getMagnitude() { return squareRoot( x squared plus y squared) }
is that a getter?
Bottom line: pattern matching on names of fields and methods will give you candidates, but you have to devise the matching rules that fit your needs.

Categories