Skip to content

Commit

Permalink
Implement #375
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Mar 17, 2014
1 parent 9f6638d commit 478d4a4
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 36 deletions.
1 change: 1 addition & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Version: 2.4.0 (xx-xxx-2014)
#353: Problems with polymorphic types, `JsonNode` (related to #88)
(reported by cemo@github)
#369: Incorrect comparison for renaming in `POJOPropertyBuilder`
#375: Add `readValue()`/`readPropertyValue()` methods in `DeserializationContext`
#381: Allow inlining/unwrapping of value from single-component JSON array
(contributed by yinzara@github)
#390: Change order in which managed/back references are resolved (now back-ref
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ protected DeserializationContext(DeserializationContext src,
* Constructor used for creating actual per-call instances.
*/
protected DeserializationContext(DeserializationContext src,
DeserializationConfig config, JsonParser jp,
DeserializationConfig config, JsonParser p,
InjectableValues injectableValues)
{
_cache = src._cache;
Expand All @@ -182,7 +182,7 @@ protected DeserializationContext(DeserializationContext src,
_config = config;
_featureFlags = config.getDeserializationFeatures();
_view = config.getActiveView();
_parser = jp;
_parser = p;
_injectableValues = injectableValues;
_attributes = config.getAttributes();
}
Expand Down Expand Up @@ -321,7 +321,7 @@ public Locale getLocale() {
public TimeZone getTimeZone() {
return _config.getTimeZone();
}

/*
/**********************************************************
/* Public API, pass-through to DeserializerCache
Expand Down Expand Up @@ -362,16 +362,15 @@ public boolean hasValueDeserializerFor(JavaType type, AtomicReference<Throwable>
*/
@SuppressWarnings("unchecked")
public final JsonDeserializer<Object> findContextualValueDeserializer(JavaType type,
BeanProperty property) throws JsonMappingException
BeanProperty prop) throws JsonMappingException
{
JsonDeserializer<Object> deser = _cache.findValueDeserializer(this,
_factory, type);
JsonDeserializer<Object> deser = _cache.findValueDeserializer(this, _factory, type);
if (deser != null) {
deser = (JsonDeserializer<Object>) handleSecondaryContextualization(deser, property);
deser = (JsonDeserializer<Object>) handleSecondaryContextualization(deser, prop);
}
return deser;
}

/**
* Method for finding a deserializer for root-level value.
*/
Expand All @@ -397,16 +396,16 @@ public final JsonDeserializer<Object> findRootValueDeserializer(JavaType type)
/**
* Convenience method, functionally same as:
*<pre>
* getDeserializerProvider().findKeyDeserializer(getConfig(), propertyType, property);
* getDeserializerProvider().findKeyDeserializer(getConfig(), prop.getType(), prop);
*</pre>
*/
public final KeyDeserializer findKeyDeserializer(JavaType keyType,
BeanProperty property) throws JsonMappingException {
BeanProperty prop) throws JsonMappingException {
KeyDeserializer kd = _cache.findKeyDeserializer(this,
_factory, keyType);
// Second: contextualize?
if (kd instanceof ContextualKeyDeserializer) {
kd = ((ContextualKeyDeserializer) kd).createContextual(this, property);
kd = ((ContextualKeyDeserializer) kd).createContextual(this, prop);
}
return kd;
}
Expand Down Expand Up @@ -543,17 +542,17 @@ public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated,
* to handle details of resolving
* {@link ContextualDeserializer} with given property context.
*
* @param property Property for which the given primary deserializer is used; never null.
* @param prop Property for which the given primary deserializer is used; never null.
*
* @since 2.3
*/
public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> deser,
BeanProperty property)
BeanProperty prop)
throws JsonMappingException
{
if (deser != null) {
if (deser instanceof ContextualDeserializer) {
deser = ((ContextualDeserializer) deser).createContextual(this, property);
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
}
}
return deser;
Expand All @@ -570,19 +569,16 @@ public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> de
* (or, in case of root value property, to any property), annotations
* accessible may or may not be relevant.
*
* @param property Property for which deserializer is used, if any; null
* @param prop Property for which deserializer is used, if any; null
* when deserializing root values
*
* @since 2.3
*/
public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser,
BeanProperty property)
throws JsonMappingException
{
if (deser != null) {
if (deser instanceof ContextualDeserializer) {
deser = ((ContextualDeserializer) deser).createContextual(this, property);
}
BeanProperty prop)
throws JsonMappingException {
if (deser != null && (deser instanceof ContextualDeserializer)) {
deser = ((ContextualDeserializer) deser).createContextual(this, prop);
}
return deser;
}
Expand All @@ -603,8 +599,7 @@ public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?>
* date format is cloned, and cloned instance will be retained
* for use during this deserialization round.
*/
public Date parseDate(String dateStr)
throws IllegalArgumentException
public Date parseDate(String dateStr) throws IllegalArgumentException
{
try {
DateFormat df = getDateFormat();
Expand All @@ -618,16 +613,69 @@ public Date parseDate(String dateStr)
* Convenience method for constructing Calendar instance set
* to specified time, to be modified and used by caller.
*/
public Calendar constructCalendar(Date d)
{
/* 08-Jan-2008, tatu: not optimal, but should work for the
* most part; let's revise as needed.
*/
public Calendar constructCalendar(Date d) {
// 08-Jan-2008, tatu: not optimal, but should work for the most part; let's revise as needed.
Calendar c = Calendar.getInstance(getTimeZone());
c.setTime(d);
return c;
}

/*
/**********************************************************
/* Convenience methods for reading parsed values
/**********************************************************
*/

/**
* Convenience method that may be used by composite or container deserializers,
* for reading one-off values contained (for sequences, it is more efficient
* to actually fetch deserializer once for the whole collection).
*<p>
* NOTE: when deserializing values of properties contained in composite types,
* rather use {@link #readPropertyValue(JsonParser, BeanProperty, Class)};
* this method does not allow use of contextual annotations.
*
* @since 2.4
*/
public <T> T readValue(JsonParser p, Class<T> type) throws IOException {
return readValue(p, getTypeFactory().constructType(type));
}

/**
* @since 2.4
*/
@SuppressWarnings("unchecked")
public <T> T readValue(JsonParser p, JavaType type) throws IOException {
JsonDeserializer<Object> deser = findRootValueDeserializer(type);
if (deser == null) {
}
return (T) deser.deserialize(p, this);
}

/**
* Convenience method that may be used by composite or container deserializers,
* for reading one-off values for the composite type, taking into account
* annotations that the property (passed to this method -- usually property that
* has custom serializer that called this method) has.
*
* @since 2.4
*/
public <T> T readPropertyValue(JsonParser p, BeanProperty prop, Class<T> type) throws IOException {
return readPropertyValue(p, prop, getTypeFactory().constructType(type));
}

/**
* @since 2.4
*/
@SuppressWarnings("unchecked")
public <T> T readPropertyValue(JsonParser p, BeanProperty prop, JavaType type) throws IOException {
JsonDeserializer<Object> deser = findContextualValueDeserializer(type, prop);
if (deser == null) {

}
return (T) deser.deserialize(p, this);
}

/*
/**********************************************************
/* Methods for problem handling, reporting
Expand All @@ -645,15 +693,15 @@ public Calendar constructCalendar(Date d)
* Method deserializers can call to inform configured {@link DeserializationProblemHandler}s
* of an unrecognized property.
*/
public boolean handleUnknownProperty(JsonParser jp, JsonDeserializer<?> deser,
public boolean handleUnknownProperty(JsonParser p, JsonDeserializer<?> deser,
Object instanceOrClass, String propName)
throws IOException, JsonProcessingException
{
LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
if (h != null) {
while (h != null) {
// Can bail out if it's handled
if (h.value().handleUnknownProperty(this, jp, deser, instanceOrClass, propName)) {
if (h.value().handleUnknownProperty(this, p, deser, instanceOrClass, propName)) {
return true;
}
h = h.next();
Expand Down Expand Up @@ -785,8 +833,8 @@ public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue
* Helper method for indicating that the current token was expected to be another
* token.
*/
public JsonMappingException wrongTokenException(JsonParser jp, JsonToken expToken, String msg) {
return JsonMappingException.from(jp, "Unexpected token ("+jp.getCurrentToken()+"), expected "+expToken+": "+msg);
public JsonMappingException wrongTokenException(JsonParser p, JsonToken expToken, String msg) {
return JsonMappingException.from(p, "Unexpected token ("+p.getCurrentToken()+"), expected "+expToken+": "+msg);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.fasterxml.jackson.databind.deser;

import java.io.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.*;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
Expand Down Expand Up @@ -144,14 +146,98 @@ public CustomKey deserializeKey(String key, DeserializationContext ctxt) throws
return new CustomKey(Integer.valueOf(key));
}
}

// [#375]

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Negative { }

static class Bean375Wrapper {
@Negative
public Bean375Outer value;
}

static class Bean375Outer {
protected Bean375Inner inner;

public Bean375Outer(Bean375Inner v) { inner = v; }
}

static class Bean375Inner {
protected int x;

public Bean375Inner(int x) { this.x = x; }
}

static class Bean375OuterDeserializer extends StdDeserializer<Bean375Outer>
implements ContextualDeserializer
{
protected BeanProperty prop;

protected Bean375OuterDeserializer() { this(null); }
protected Bean375OuterDeserializer(BeanProperty p) {
super(Bean375Outer.class);
prop = p;
}

@Override
public Bean375Outer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
Object ob = ctxt.readPropertyValue(p, prop, Bean375Inner.class);
return new Bean375Outer((Bean375Inner) ob);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
return new Bean375OuterDeserializer(property);
}
}

static class Bean375InnerDeserializer extends StdDeserializer<Bean375Inner>
implements ContextualDeserializer
{
protected boolean negative;

protected Bean375InnerDeserializer() { this(false); }
protected Bean375InnerDeserializer(boolean n) {
super(Bean375Inner.class);
negative = n;
}

@Override
public Bean375Inner deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
int x = jp.getIntValue();
if (negative) {
x = -x;
} else {
x += x;
}
return new Bean375Inner(x);
}

@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {
if (property != null) {
Negative n = property.getAnnotation(Negative.class);
if (n != null) {
return new Bean375InnerDeserializer(true);
}
}
return this;
}

}

/*
/**********************************************************
/* Unit tests
/**********************************************************
*/

final ObjectMapper MAPPER = new ObjectMapper();
final ObjectMapper MAPPER = objectMapper();

public void testCustomBeanDeserializer() throws Exception
{
Expand Down Expand Up @@ -226,4 +312,24 @@ public void testIssue882() throws Exception
assertNotNull(deserialized.map);
assertEquals(1, deserialized.map.size());
}

// [#337]: convenience methods for custom deserializers to use
public void testContextReadValue() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addDeserializer(Bean375Outer.class, new Bean375OuterDeserializer());
module.addDeserializer(Bean375Inner.class, new Bean375InnerDeserializer());
mapper.registerModule(module);

// First, without property; doubles up value:
Bean375Outer outer = mapper.readValue("13", Bean375Outer.class);
assertEquals(26, outer.inner.x);

// then with property; should find annotation, turn negative
Bean375Wrapper w = mapper.readValue("{\"value\":13}", Bean375Wrapper.class);
assertNotNull(w.value);
assertNotNull(w.value.inner);
assertEquals(-13, w.value.inner.x);
}
}

0 comments on commit 478d4a4

Please sign in to comment.