Implemented a new style of JSON serialization and deserialization by registering Repository-aware components with Jackson's underlying ObjectMapper. This has the benefit of allowing us to transliterate domain objects into link representations and back again, no matter where those entities appear in complex nested object graphs.
This commit is contained in:
@@ -9,7 +9,6 @@ import org.codehaus.jackson.annotate.JsonTypeInfo;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, defaultImpl = ResourceLink.class)
|
||||
public interface Link {
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.springframework.data.rest.core;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Link}.
|
||||
@@ -33,6 +33,25 @@ public class ResourceLink implements Link, Comparable<Link> {
|
||||
return href;
|
||||
}
|
||||
|
||||
public String getRel() {
|
||||
return rel();
|
||||
}
|
||||
|
||||
public ResourceLink setRel(String rel) {
|
||||
this.rel = rel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public URI getHref() {
|
||||
return href();
|
||||
}
|
||||
|
||||
public ResourceLink setHref(URI href) {
|
||||
Assert.notNull(href, "href URI cannot be null.");
|
||||
this.href = href;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public int compareTo(Link link) {
|
||||
if(null == rel || null == link.rel()) {
|
||||
return -1;
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package org.springframework.data.rest.core.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.JsonToken;
|
||||
import org.codehaus.jackson.map.DeserializationContext;
|
||||
import org.codehaus.jackson.map.deser.std.StdDeserializer;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A "fluent" bean is one that does not use the JavaBean conventions of "setProperty" and "getProperty" but instead
|
||||
* uses just "property" with 0 or 1 arguments to distinguish between getter (0 arg) and setter (1 arg).
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class FluentBeanDeserializer extends StdDeserializer {
|
||||
|
||||
private ConversionService conversionService;
|
||||
private FluentBeanUtils.Metadata beanMeta;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public FluentBeanDeserializer(final Class<?> valueClass, ConversionService conversionService) {
|
||||
super(valueClass);
|
||||
this.conversionService = conversionService;
|
||||
this.beanMeta = FluentBeanUtils.metadata(valueClass);
|
||||
|
||||
if(!FluentBeanUtils.isFluentBean(valueClass)) {
|
||||
throw new IllegalArgumentException("Class of type " + valueClass + " is not a FluentBean");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(JsonParser jp,
|
||||
DeserializationContext ctxt) throws IOException {
|
||||
if(jp.getCurrentToken() != JsonToken.START_OBJECT) {
|
||||
throw ctxt.mappingException(_valueClass);
|
||||
}
|
||||
|
||||
Object bean;
|
||||
try {
|
||||
bean = _valueClass.newInstance();
|
||||
} catch(InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
while(jp.nextToken() != JsonToken.END_OBJECT) {
|
||||
String name = jp.getCurrentName();
|
||||
Method setter = beanMeta.setters().get(name);
|
||||
|
||||
Object obj;
|
||||
if(null != setter) {
|
||||
Class<?> targetType = setter.getParameterTypes()[0];
|
||||
if(ClassUtils.isAssignable(targetType, Long.class)) {
|
||||
obj = jp.nextLongValue(-1);
|
||||
} else if(ClassUtils.isAssignable(targetType, Integer.class)) {
|
||||
obj = jp.nextIntValue(-1);
|
||||
} else if(ClassUtils.isAssignable(targetType, Boolean.class)) {
|
||||
obj = jp.nextBooleanValue();
|
||||
} else {
|
||||
obj = jp.nextTextValue();
|
||||
}
|
||||
|
||||
if(null != obj) {
|
||||
if(!ClassUtils.isAssignable(obj.getClass(), targetType)) {
|
||||
obj = conversionService.convert(obj, targetType);
|
||||
}
|
||||
|
||||
try {
|
||||
setter.invoke(bean, obj);
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch(InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.springframework.data.rest.core.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.JsonGenerator;
|
||||
import org.codehaus.jackson.map.SerializerProvider;
|
||||
import org.codehaus.jackson.map.ser.std.SerializerBase;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A "fluent" bean is one that does not use the JavaBean conventions of "setProperty" and "getProperty" but instead
|
||||
* uses just "property" with 0 or 1 arguments to distinguish between getter (0 arg) and setter (1 arg).
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class FluentBeanSerializer
|
||||
extends SerializerBase<Object> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public FluentBeanSerializer(final Class t) {
|
||||
super(t);
|
||||
|
||||
if(!FluentBeanUtils.isFluentBean(t)) {
|
||||
throw new IllegalArgumentException("Class of type " + t + " is not a FluentBean");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public void serialize(final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider)
|
||||
throws IOException,
|
||||
JsonGenerationException {
|
||||
if(null == value) {
|
||||
provider.defaultSerializeNull(jgen);
|
||||
} else {
|
||||
Class<?> type = value.getClass();
|
||||
if(ClassUtils.isAssignable(type, Collection.class)) {
|
||||
jgen.writeStartArray();
|
||||
for(Object o : (Collection)value) {
|
||||
write(o, jgen, provider);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
} else if(ClassUtils.isAssignable(type, Map.class)) {
|
||||
jgen.writeStartObject();
|
||||
for(Map.Entry<String, Object> entry : ((Map<String, Object>)value).entrySet()) {
|
||||
jgen.writeFieldName(entry.getKey());
|
||||
write(entry.getValue(), jgen, provider);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
} else {
|
||||
write(value, jgen, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void write(final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider)
|
||||
throws IOException {
|
||||
Class<?> type = value.getClass();
|
||||
if(ClassUtils.isAssignable(type, _handledType)) {
|
||||
jgen.writeStartObject();
|
||||
for(String fname : FluentBeanUtils.metadata(type).fieldNames()) {
|
||||
jgen.writeFieldName(fname);
|
||||
write(FluentBeanUtils.get(fname, value), jgen, provider);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
} else {
|
||||
jgen.writeObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user