Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,82 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ActionInvocation;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.interceptor.parameter.StrutsParameter;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Map;

/**
* Handles JSON content using jackson-lib
*/
public class JacksonJsonHandler implements ContentTypeHandler {

private static final Logger LOG = LogManager.getLogger(JacksonJsonHandler.class);
private static final String DEFAULT_CONTENT_TYPE = "application/json";
private String defaultEncoding = "ISO-8859-1";
private ObjectMapper mapper = new ObjectMapper();
private boolean requireAnnotations = false;

@Inject(value = StrutsConstants.STRUTS_PARAMETERS_REQUIRE_ANNOTATIONS, required = false)
public void setRequireAnnotations(String requireAnnotations) {
this.requireAnnotations = org.apache.commons.lang3.BooleanUtils.toBoolean(requireAnnotations);
}

@Override
public void toObject(ActionInvocation invocation, Reader in, Object target) throws IOException {
mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
ObjectReader or = mapper.readerForUpdating(target);
or.readValue(in);
if (requireAnnotations) {
// Deserialize into a map first, then filter by @StrutsParameter annotation
@SuppressWarnings("unchecked")
Map<String, Object> jsonMap = mapper.readValue(in, Map.class);
applyAnnotatedProperties(target, jsonMap);
} else {
ObjectReader or = mapper.readerForUpdating(target);
or.readValue(in);
}
}

/**
* Sets only properties whose setter method is annotated with @StrutsParameter,
* consistent with ParametersInterceptor behavior for URL parameters.
*/
private void applyAnnotatedProperties(Object target, Map<String, Object> jsonMap) {
try {
BeanInfo info = Introspector.getBeanInfo(target.getClass());
PropertyDescriptor[] props = info.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
String name = prop.getName();
if (!jsonMap.containsKey(name)) {
continue;
}
Method setter = prop.getWriteMethod();
if (setter == null) {
continue;
}
if (setter.getAnnotation(StrutsParameter.class) == null) {
LOG.debug("REST JSON property '{}' rejected: setter [{}] missing @StrutsParameter annotation",
name, setter.getName());
continue;
}
// Use Jackson to convert the value to the correct type and set it
Object value = jsonMap.get(name);
Object converted = mapper.convertValue(value, setter.getParameterTypes()[0]);
setter.invoke(target, converted);
}
} catch (Exception e) {
LOG.error("Error applying annotated properties from JSON", e);
}
}

@Override
Expand Down
Loading