blob: 357dc9bc4fdafdf3cf3c134aefac39f2c19baae4 [file] [log] [blame]
// ============================================================
objectMapper.setSerializerFactory(new CompactBeanSerializerFactory(objectMapper));
// ============================================================
/** Parses an empty object using readValue("{}", type). */
private static Object defaultInstance(Class<?> type, ObjectMapper objectMapper) {
try {
return objectMapper.readValue(EMPTY_OBJECT_JSON, type);
// EMPTY_OBJECT_JSON == "{}", set in a static initializer by getting
// a JsonGenerator and calling writeStartObject and writeEndObject.
} catch (IOException e) {
// hmm, shouldn't readValue(String,...) throw only JsonProcessingExceptions, not general IOE?
throw new RuntimeException("cannot parse " + EMPTY_OBJECT_JSON + " as " + type, e);
}
}
// ============================================================
class CompactBeanSerializerFactory extends CustomSerializerFactory {
private final ObjectMapper mapper;
CompactBeanSerializerFactory(ObjectMapper mapper) { this.mapper = mapper; }
@Override public JsonSerializer<Object> findBeanSerializer(Class<?> type, SerializationConfig config) {
// code for caching already created serializers omitted
JsonSerializer<Object> serializer = super.findBeanSerializer(type, config);
return serializer instanceof BeanSerializer
? new CompactBeanSerializer(type, mapper, (BeanSerializer) serializer,
findBeanProperties(config, (BasicBeanDescription) config.introspect(type)) )
: serializer;
}
}
// ============================================================
class CompactBeanSerializer extends JsonSerializer<Object> {
private final Class<?> type;
private final BeanSerializer beanSerializer;
private final Collection<? extends BeanPropertyWriter> properties;
private final ObjectMapper objectMapper;
CompactBeanSerializer(Class<?> type, ObjectMapper objectMapper, BeanSerializer beanSerializer,
Collection<? extends BeanPropertyWriter> properties) { /* set all fields */ }
@Override public void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider) {
try {
// -- build collection of initialized properties
List<BeanPropertyWriter> setProperties = new ArrayList<BeanPropertyWriter>(properties.size());
JsonGenerator nullGen = objectMapper.getJsonFactory().createJsonGenerator(NullWriter.INSTANCE);
nullGen.writeStartObject();
StoringSerializerProvider storingSP = new StoringSerializerProvider(provider);
Object defaultValuesBean = defaultInstance(bean.getClass(), objectMapper);
for (BeanPropertyWriter prop : properties) {
prop.serializeAsField(defaultValuesBean, nullGen, storingSP);
Object defaultValue = storingSP.getAndResetLastValue();
prop.serializeAsField(bean, nullGen, storingSP);
Object value = storingSP.getAndResetLastValue();
boolean equals = defaultValue == value || (defaultValue != null && defaultValue.equals(value));
if (!equals) { setProperties.add(prop); }
}
// -- serialize only changed properties
if (setProperties.size() != properties.size()) {
if (setProperties.isEmpty()) {
jgen.writeStartObject();
jgen.writeEndObject();
} else {
new BeanSerializer(bean.getClass(), setProperties).serialize(bean, jgen, provider);
}
return;
}
} catch (Exception e) { // OK, invoke default impl
logger.warn("failed to determine which properties are set for " + bean, e);
}
beanSerializer.serialize(bean, jgen, provider);
}
}
// ============================================================
/** A null device which does nothing. */
class NullWriter extends Writer { ... }
// ============================================================
/** Decorates a given SerializerProvider to store the most recently serialized value. */
class StoringSerializerProvider extends SerializerProvider {
private final SerializerProvider decoratee;
private Object lastValue;
StoringSerializerProvider(SerializerProvider decoratee) {
super(decoratee.getConfig());
this.decoratee = decoratee;
}
Object getAndResetLastValue() { Object v = lastValue; lastValue = null; return v; }
@Override public JsonSerializer<Object> findValueSerializer(Class<?> type) {
final JsonSerializer<Object> defaultSerializer = decoratee.findValueSerializer(type);
return new JsonSerializer<Object>() {
@Override public void serialize(Object value, JsonGenerator g, SerializerProvider p) {
lastValue = value;
defaultSerializer.serialize(value, g, p);
} };
}
@Override public void defaultSerializeDateValue(long timestamp, JsonGenerator g) {
lastValue = timestamp;
decoratee.defaultSerializeDateValue(timestamp, g);
}
// Also set lastValue in defaultSerializeDateValue(Date, JsonGenerator) and
// serializeValue(SerializationConfig, JsonGenerator, Object, SerializerFactory)
// Other methods just delegate to decoratee.
}