Serialize a class based on a single interface, which it implements using Jackson or Gson

I have the following:

Interface I1 extends Ia, Ib, Ic

Interface I2.

Class C implements I1, I2. And this class has its own setters and getters.

C cInstance = new C ():

//Jackson ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(new File("somefile.json"), cInstance); //Gson Gson gson = new Gson(); String json = gson.toJson(cInstance); 

The result will be cInstance serialized according to C properties and what it inherited. However, I like that the properties are serialized according to the setters / getters in I1 (only the cInstance properties presented in the I1 interface).

How can I do this with Jackson, knowing that I have too many classes with the same problem, and I can’t change the class definition or add annotations.

And the same problem applies to deserialization (interface deserialization)

thanks

+4
source share
2 answers

First of all, you can always attach “mixing annotations” even without directly adding annotations (see the wiki page ). In this case, the annotation for use will be:

 @JsonSerialize(as=MyInterface.class) 

but if you do not want to use mix-ins, you can force a specific type with

 objectMapper.typedWriter(MyInterface.class).writeValue(....) 
+2
source

Jackson VisibilityChecker provides an easy way to filter certain properties, especially because it allows you to check visibility (whether it will be serialized or not) for each method / field separately.

At the very least, it helps for the serialization phase.

Here is what I did (using Jackson version 1.9.11):

 import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.introspect.AnnotatedMethod; import org.codehaus.jackson.map.introspect.VisibilityChecker; public static class InterfaceVisibilityChecker extends VisibilityChecker.Std { private final Set<Method> visibleMethods; public InterfaceVisibilityChecker(Class<?>... clazzes) { super(JsonAutoDetect.Visibility.PUBLIC_ONLY); this.visibleMethods = new HashSet<>(); for (Class<?> clz : clazzes) { this.visibleMethods.addAll(Arrays.asList(clz.getMethods())); } } @Override public boolean isGetterVisible(Method m) { return super.isGetterVisible(m) && isVisible(m); } @Override public boolean isGetterVisible(AnnotatedMethod m) { return isGetterVisible(m.getAnnotated()); } private boolean isVisible(Method m) { for (Method visiMthd : visibleMethods) { if (isOverwriteMethod(m, visiMthd)) return true; } return false; } private boolean isOverwriteMethod(Method subMethod, Method superMethod) { // names must be equal if (! subMethod.getName().equals(superMethod.getName())) return false; // return types must be assignable if (! superMethod.getReturnType().isAssignableFrom(subMethod.getReturnType())) return false; // parameters must be equal if (! Arrays.equals(subMethod.getParameterTypes(), superMethod.getGenericParameterTypes())) return false; // classes must be assignable return superMethod.getDeclaringClass().isAssignableFrom(subMethod.getDeclaringClass()); } } 

The basic idea is to use the standard VisibilityChecker and extend it by checking whether the method is declared in one of the specified interfaces.

The controller is applied to the ObjectMapper instance using the following snippet:

 ObjectMapper om = new ObjectMapper(); om.setVisibilityChecker(new InterfaceVisibilityChecker( I1.class, I2.class, Ia.class, Ib.class, Ic.class 

));

Some comments on the solution above:

  • Validation is not complete; methods like isIsGetterVisible or isFieldVisible can be handled in the same way if necessary.
  • isOverwriteMethod is not optimized at all, checks can be cached.
0
source

Source: https://habr.com/ru/post/953313/


All Articles