Search code examples
javajsonjacksonobjectmapper

ObjectMapper properties set in a generic way?


I am writing a class JsonUtils which will contain different functions to serialize and deserialize data.

 public class JsonUtils {

        private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

        public static String toJsonString(Object obj)  {

            String json = null;

            JSON_MAPPER.setPropertyNamingStrategy(new CustomNamingStrategy());
            JSON_MAPPER.setSerializationInclusion(Inclusion.NON_NULL);

            try {
                System.out.print("OBJECT MAPPER:---> JSON STRING:\n" + JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj));
                json = JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            } catch (JsonGenerationException e) {
                e.printStackTrace();
            } catch (JsonMappingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return json;
        }

        public static <T> T toPOJO(String json, Class<T> type){

            JSON_MAPPER.setPropertyNamingStrategy(new CustomNameNamingStrategy());
            System.out.println("TO POJO: Json string " + json);
            try {

                return JSON_MAPPER.readValue(json, type);

            } catch (JsonParseException e) {
                e.printStackTrace();
            } catch (JsonMappingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

Now, I want the functions to be used generically. For ex: someone wants to call toJsonString method but wants to use a different naming strategy to convert to json. Or may want to add some other properties to ObjectMapper like register a module.

Currently, the ObjectMapper properties are being set inside the function, thus a new naming strategy or a different property for ObjectMapper can't be used.

Is there a way that every user for JsonUtils initially sets it's own properties for ObjectMapper ? Or a efficient and generic way to write my Utility class ?


Solution

  • You could use something like this:

    ObjectMapperProperties.java

    package example;
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    
            public class ObjectMapperProperties {
    
                private PropertyNamingStrategy propertyNamingStrategy;
    
                private ObjectMapperProperties(final PropertyNamingStrategy propertyNamingStrategy) {
                    this.propertyNamingStrategy = propertyNamingStrategy;
                }
    
                public PropertyNamingStrategy getPropertyNamingStrategy() {
                    return propertyNamingStrategy;
                }
    
                public static class ObjectMapperPropertiesBuilder {
    
                    private PropertyNamingStrategy builderPropertyNamingStrategy;
    
                    public ObjectMapperPropertiesBuilder() {
    
                    }
    
                    public ObjectMapperPropertiesBuilder setPropertyNamingStrategy(final PropertyNamingStrategy builderPropertyNamingStrategy) {
                        this.builderPropertyNamingStrategy = builderPropertyNamingStrategy; 
                        return this;
                    }
    
                    public ObjectMapperProperties build() {
                        return new ObjectMapperProperties(builderPropertyNamingStrategy);
                    }
    
                }
        }
    

    ObjectMapperFactory.java

    package example;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class ObjectMapperFactory {
    
        public static ObjectMapper getObjectMapper(final ObjectMapperProperties objectMapperProperties) {
            final ObjectMapper result = new ObjectMapper();
            result.setPropertyNamingStrategy(objectMapperProperties.getPropertyNamingStrategy());
            return result;
        }
    
    }
    

    Client.class

    package example;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.PropertyNamingStrategy;
    
    import example.ObjectMapperProperties.ObjectMapperPropertiesBuilder;
    
    public class Client {
    
        public static void main(String[] args) {
            ObjectMapperPropertiesBuilder objectMapperPropertiesBuilder = new ObjectMapperPropertiesBuilder();
            objectMapperPropertiesBuilder.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
            ObjectMapperFactory factory = new ObjectMapperFactory();
            ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(objectMapperPropertiesBuilder.build());
    
        }
    
    }
    

    Than you could create ObjectMapper with setting as you need it. It doesn't have a sense and it's error-prone to set properties twice on already created instance.

    private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
    JSON_MAPPER.setSerializationInclusion(Inclusion.NON_NULL);
    

    so next time you need to reset this property for example, but to create new ObjectMapper() through some factory is priceless and less error-prone

    Answers:

    No you will create new instance of ObjectMapper through ObjectMapperFactory for each call and just pass the ObjectMapperProperties.

    public static String toJsonString(Object obj,final ObjectMapperProperties objectMapperProperties)  {
    
    ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(objectMapperProperties);
    }
    

    In case you don't want to create new ObjectMapper instance and properties are final (mean you will always create ObjectMapper with same properties) than make a method.

    public static String toJsonString(Object obj, ObjectMapper objMapper)  {}
    

    Second question see Builder Pattern

    For better testing variation with Factory as interface will be helpful:

    ObjectMapperFactory.class

    public interface ObjectMapperFactory {
    
        public ObjectMapper getObjectMapper(final ObjectMapperProperties objectMapperProperties) {
    }
    

    Implementation of ObjectMapperFactory

    ObjectMapperFactoryImpl.class

    package example;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class ObjectMapperFactoryImpl implements ObjectMapperFactory {
    
        public ObjectMapper getObjectMapper(final ObjectMapperProperties objectMapperProperties) {
            final ObjectMapper result = new ObjectMapper();
            result.setPropertyNamingStrategy(objectMapperProperties.getPropertyNamingStrategy());
            return result;
        }
    
    }
    

    and in your class

     public class JsonUtils {
    
          private final ObjectMapperFactory objectMapperFactory;
    
          public JsonUtils(final ObjectMapperFactory objectMapperFactory) {
                  this.objectMapperFactory = objectMapperFactory;
          }
    }
    

    But that is just a variantion. For your purposes answer posted above is enough.