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 ?
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.