Use JsonIgnore for an attribute in an other class

Viewed 891

I meet an issue with a class contained in a library that I use.

This issue comes when I want deserialize it.

Indeed, this class has a method names "getCopy" which returns a new instance of himself which contains this same method and call it still a StackOverFlowException on the following cycle :

at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:166)

at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)

at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:723)

public class Object {
   ...
   ObjectAttribute objectAttribute;
   ...

   public ObjectAttribute getObjectAttribute(){
      return this.objectAttribute
   }
   ...
}

public class ObjectAttribute{
   ...

   public ObjectAttribute getCopy{
      return copy(this) //return a new instance of himself
   }
   ...
}

Is there a way to ignore the method getCopy() like @JsonIgnoreAttribute("objectProperty.copy")?

5 Answers

For this specific use case, when you have a class in a third party library that you are not able to modify, Jackson provides the Mix-in annotations.

The idea behind this concept is to provide a class that indicates how the serialization of another class should be accomplished.

For instance, consider the following mix-in class definition for your use case:

public abstract class ObjectAttributeMixIn{
   // You need to provide definitions for every property you need
   // to serialize, and the proper constructor if necessary
   ...

   // Ignore the getCopy method
   @JsonIgnore
   public abstract ObjectAttribute getCopy();

   ...
}

You can use the full set of Jackson annotations in the mix-in definitions.

Then, associate the mix-in with the ObjectAttribute class. You can use the instance of ObjectMapper you are using for serialization for this purpose:

objectMapper.addMixInAnnotations(ObjectAttribute.class, ObjectAttributeMixIn.class);

Yon can also register a custom module instead; please, see the relevant documentation.

for ignore method getCopy, just enough rename this method , e.g copy

every method start with get then serialized ,e.g if method name is getSomething then serialized to something: (return value by method))

so if you change method name to copy or copyInstance or every name without start by get then method not serialized

You can override JsonSerializer and do specific logic for class

public class CustomSerializerForC extends JsonSerializer<C> {
   @Override
   public Class<C> handledType() {
       return C.class;
   }
  @Override
   public void serialize(C c, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
       String upperCase = c.getValue().toUpperCase();
       jsonGenerator.writeString(upperCase);
   }
}

And use Serializer in moudle used in ObjectMapper:

   SimpleModule module = new SimpleModule("MyCustomModule", new Version(1, 0, 0, null));
   module.addSerializer(new CustomSerializerForC());
   ObjectMapper mapper = new ObjectMapper();
   mapper.registerModule(module);

You can register serializer and choose the fields you would like

 /**
 * We can not change source code so we are adding serializer for a specific type.
 *
 */
public static class JsonSpecificTypeSerializer extends JsonSerializer<SpecificType> {

    @Override
    public void serialize(SpecificType t, JsonGenerator jsonGen, SerializerProvider serializerProvider) throws IOException {
        jsonGen.writeStartObject();
        jsonGen.writeFieldName("field1");
        jsonGen.writeNumber(t.getield1());
        .......

        jsonGen.writeEndObject();
    }

}

/**
 * Customize jackson.
 *
 * adding configuration to jackson without overriding spring boot default conf.
 */
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizeJackson() {
    return jacksonObjectMapperBuilder -> {
        jacksonObjectMapperBuilder.serializerByType(SpecificType.class,
                new JsonSpecificTypeSerializer());
    };
}

There are 2 ways I see how to figure out your issue:

  1. Write custom deserializer for you specific class and register it in Jackson mapper.
  2. Tune up global Jackson mapper to ignore class getters in auto-detection and use only fields.

Please try 2 way with following config:

ObjectMapper mapper = new ObjectMapper();

mapper.setVisibility(JsonMethod.ALL, Visibility.NONE);
mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);

If you decide to move forward with 1 way, please write here if you need help.

Related