/*
* This file is part of the GeoLatte project. This code is licenced under
* the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and limitations under the
* License.
*
* Copyright (C) 2010 - 2010 and Ownership of code is shared by:
* Qmino bvba - Romeinsestraat 18 - 3001 Heverlee (http://www.Qmino.com)
* Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com)
*/
package org.geolatte.common.dataformats.json.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* The abstract JsonDeserializer is a convenience class allowing the implementor of an actual
* JSON deserializer to more quickly implement the deserializer. The deserializer will first deserialize
* the object to a {@code Map<String,Object>}, and then provides a number of convenient methods to get the values
* of the parameters you need.
*
* @author Yves Vandewoude
* class org.geolatte.core.dataformats.json.AbstractJsonDeserializer
* company <a href="http://www.Qmino.com">Qmino</a>
* Creation-Date: 20-okt-2009
* Creation-Time: 10:16:22
*/
public abstract class AbstractJsonDeserializer<T> extends JsonDeserializer<T> {
protected JsonMapper parent;
/**
* The abstract json deserializer first parses the json to a HashMap. To ensure threadsafety
* the 'current' object being parsed is kept as a threadlocal field.
*/
private static final ThreadLocal inputParams = new ThreadLocal() {
@Override
protected synchronized Object initialValue() {
return null;
}
};
public AbstractJsonDeserializer(JsonMapper owner) {
parent = owner;
}
@Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
// Ensure that no problems arrise when recursive calls occur.
Map<String, Object> originalParams = (Map<String, Object>) inputParams.get();
try {
Map<String, Object> newValue = (Map<String, Object>) jp.readValueAs(Object.class);
inputParams.set(newValue);
} catch (ClassCastException exc) {
throw new IOException("Expected a JSON Object, but this was not the case. ", exc);
}
T answer = deserialize(jp);
inputParams.set(originalParams);
return answer;
}
/**
* The actual method to override when implementing the deserializer
*
* @param jsonParser the actual parser. Provided for flexibility, but unlikely to be needed as all properties are
* already available.
* @return A deserialized instance of the given json string
* @throws IOException if the deserialization failed.
*/
protected abstract T deserialize(JsonParser jsonParser) throws IOException;
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param mapToUse the map to use as input parametersource
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected String getStringParam(String paramName, String errorMessage, Map<String, Object> mapToUse)
throws IOException {
return getTypedParam(paramName, errorMessage, String.class, mapToUse);
}
/**
* Convenience method for subclasses. Uses the default map for parameterinput
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected String getStringParam(String paramName, String errorMessage)
throws IOException {
return getStringParam(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param mapToUse the map to use as input parametersource
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
public String getSubJson(String paramName, String errorMessage, Map<String, Object> mapToUse) throws IOException {
Object o = mapToUse.get(paramName);
if (o != null) {
try {
return parent.toJson(o);
} catch (JsonException e) {
throw new IOException(errorMessage, e);
}
} else {
if (errorMessage != null) {
throw new IOException(errorMessage);
} else {
return null;
}
}
}
/**
* Convenience method for subclasses. Uses the default parametermap for this deserializer
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
public String getSubJson(String paramName, String errorMessage) throws IOException {
return getSubJson(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method for subclasses. Retrieves a double with the given parametername, even if that
* parametercontent is actually a string containing a number or a long or an int or... Anything that
* can be converted to a double is returned. uses the default parameterhashmap from this deserializer.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Double getDoubleParam(String paramName, String errorMessage) throws IOException {
return getDoubleParam(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method for subclasses. Retrieves a double with the given parametername, even if that
* parametercontent is actually a string containing a number or a long or an int or... Anything that
* can be converted to a double is returned.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param mapToUse an external hashmap to use as inputsource. Should not be null!
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Double getDoubleParam(String paramName, String errorMessage, Map<String, Object> mapToUse)
throws IOException {
Object o = mapToUse.get(paramName);
if (o != null) {
try {
return Double.parseDouble(o.toString());
} catch (NumberFormatException ignored) {
}
}
return getTypedParam(paramName, errorMessage, Double.class, mapToUse);
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param mapToUse the map to use as inputsource for parameters. Should not be null!
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Integer getIntParam(String paramName, String errorMessage, Map<String, Object> mapToUse) throws IOException {
Object o = mapToUse.get(paramName);
if (o != null) {
try {
return Integer.parseInt(o.toString());
} catch (NumberFormatException ignored) {
}
}
return getTypedParam(paramName, errorMessage, Integer.class, mapToUse);
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Integer getIntParam(String paramName, String errorMessage) throws IOException {
return getIntParam(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param mapToUse The map to use as inputsource for parameters. Should not be null.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Boolean getBooleanParam(String paramName, String errorMessage, Map<String, Object> mapToUse) throws IOException {
Boolean result = getTypedParam(paramName, errorMessage, Boolean.class, mapToUse);
if (result == null) {
String s = getTypedParam(paramName, errorMessage, String.class, mapToUse);
if (s != null) {
return Boolean.parseBoolean(s);
}
}
return result;
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected Boolean getBooleanParam(String paramName, String errorMessage) throws IOException {
return getBooleanParam(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method for subclasses.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param clazz the class of the parameter that should be parsed.
* @param mapToUse the map to use as inputparameter source
* @param <A> the type of the parameter
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected <A> A getTypedParam(String paramName, String errorMessage, Class<A> clazz, Map<String, Object> mapToUse)
throws IOException {
Object o = mapToUse.get(paramName);
if (o != null && clazz.isAssignableFrom(o.getClass())) {
return (A) o;
} else {
if (errorMessage != null) {
throw new IOException(errorMessage);
} else {
return null;
}
}
}
/**
* Convenience method for subclasses. Uses the default parametermap for this deserializer.
*
* @param paramName the name of the parameter
* @param errorMessage the errormessage to add to the exception if the param does not exist.
* @param clazz the class of the parameter that should be parsed.
* @param <A> the type of the parameter
* @return a stringparameter with given name. If it does not exist and the errormessage is provided,
* an IOException is thrown with that message. if the errormessage is not provided, null is returned.
* @throws IOException Exception if the paramname does not exist and an errormessage is provided.
*/
protected <A> A getTypedParam(String paramName, String errorMessage, Class<A> clazz) throws IOException {
return getTypedParam(paramName, errorMessage, clazz, (Map<String, Object>) inputParams.get());
}
/**
* Convenience method. Parses a string into a double. If it can no be converted to a double, the
* defaultvalue is returned. Depending on your choice, you can allow null as output or assign it some value
* and have very convenient syntax such as: double d = parseDefault(myString, 0.0); which is a lot shorter
* than dealing with all kinds of numberformatexceptions.
*
* @param input The inputstring
* @param defaultValue The value to assign in case of error
* @return A double corresponding with the input, or defaultValue if no double can be extracted
*/
public Double parseDefault(String input, Double defaultValue) {
if (input == null) {
return defaultValue;
}
Double answer = defaultValue;
try {
answer = Double.parseDouble(input);
} catch (NumberFormatException ignored) {
}
return answer;
}
/**
* Convenience method. Parses a string into a double. If it can no be converted to a double, the
* defaultvalue is returned. Depending on your choice, you can allow null as output or assign it some value
* and have very convenient syntax such as: double d = parseDefault(myString, 0.0); which is a lot shorter
* than dealing with all kinds of numberformatexceptions.
*
* @param input The inputstring
* @param defaultValue The value to assign in case of error
* @return A double corresponding with the input, or defaultValue if no double can be extracted
*/
public Integer parseDefault(String input, Integer defaultValue) {
if (input == null) {
return defaultValue;
}
Integer answer = defaultValue;
try {
answer = Integer.parseInt(input);
} catch (NumberFormatException ignored) {
}
return answer;
}
/**
* Parses a date from either dd/MM/yyyy or yyyy-MM-dd format
*
* @param paramName the name of the parameter containing the date
* @param errorMessage the message to put in an error if one occurs
* @param mapToUse the external map that should be used as inputsource for parameters
* @return a date object correcponding with the jsonobject
* @throws IOException If something went wrong
*/
protected Date getDateParam(String paramName, String errorMessage, Map<String, Object> mapToUse) throws IOException {
String dateInString = getStringParam(paramName, errorMessage, mapToUse);
if (dateInString == null) {
if (errorMessage == null) {
return null;
} else {
throw new IOException(errorMessage);
}
}
try {
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
return formatter.parse(dateInString);
} catch (ParseException ignored) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
return formatter.parse(dateInString);
} catch (ParseException e) {
if (errorMessage == null) {
return null;
} else {
throw new IOException(errorMessage, e);
}
}
}
}
/**
* Parses a date from either dd/MM/yyyy or yyyy-MM-dd format. Uses the default parameter inputmap.
*
* @param paramName the name of the parameter containing the date
* @param errorMessage the message to put in an error if one occurs
* @return a date object correcponding with the jsonobject
* @throws IOException If something went wrong
*/
protected Date getDateParam(String paramName, String errorMessage) throws IOException {
return getDateParam(paramName, errorMessage, (Map<String, Object>) inputParams.get());
}
}