Skip to content

Commit

Permalink
Merge branch '2.7'
Browse files Browse the repository at this point in the history
Conflicts:
	src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
	src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
	src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
	src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
  • Loading branch information
cowtowncoder committed Mar 15, 2016
2 parents 0934fb7 + 493ba8d commit bb8a0b3
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 150 deletions.
3 changes: 3 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Project: jackson-databind
(reported by Xavi T)
#1154: @JsonFormat.pattern on dates is now ignored if shape is not explicitely provided
(reported by Yoann R)
#1161: `DeserializationFeature.READ_ENUMS_USING_TO_STRING` not dynamically
changeable with 2.7
(reported by asa-git@github)
- Minor fixes to `AnnotationIntrospector.findEnumValues()` to correct problems with
merging of explicit enum value names.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,8 @@ public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
}
// Need to consider @JsonValue if one found
if (deser == null) {
deser = new EnumDeserializer(constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod()));
deser = new EnumDeserializer(constructEnumResolver(enumClass,
config, beanDesc.findJsonValueMethod()));
}
}

Expand Down Expand Up @@ -1436,7 +1437,6 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, valueDesForKey);
}
}

EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
// May have @JsonCreator for static factory method:
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
Expand Down Expand Up @@ -1919,10 +1919,8 @@ protected EnumResolver constructEnumResolver(Class<?> enumClass,
}
return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor, config.getAnnotationIntrospector());
}
// May need to use Enum.toString()
if (config.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)) {
return EnumResolver.constructUnsafeUsingToString(enumClass, config.getAnnotationIntrospector());
}
// 14-Mar-2016, tatu: We used to check `DeserializationFeature.READ_ENUMS_USING_TO_STRING`
// here, but that won't do: it must be dynamically changeable...
return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,32 @@ public class EnumDeserializer
{
private static final long serialVersionUID = 1L;

protected Object[] _enumsByIndex;

/**
* @since 2.6
* @since 2.8
*/
protected final CompactStringObjectMap _enumLookup;
private final Enum<?> _enumDefaultValue;

/**
* @since 2.6
* @since 2.7.3
*/
protected Object[] _enumsByIndex;
protected final CompactStringObjectMap _lookupByName;

public EnumDeserializer(EnumResolver res)
/**
* Alternatively, we may need a different lookup object if "use toString"
* is defined.
*
* @since 2.7.3
*/
protected CompactStringObjectMap _lookupByToString;

public EnumDeserializer(EnumResolver byNameResolver)
{
super(res.getEnumClass());
_enumLookup = res.constructLookup();
_enumsByIndex = res.getRawEnums();
_enumDefaultValue = res.getDefaultValue();
super(byNameResolver.getEnumClass());
_lookupByName = byNameResolver.constructLookup();
_enumsByIndex = byNameResolver.getRawEnums();
_enumDefaultValue = byNameResolver.getDefaultValue();
}

/**
Expand Down Expand Up @@ -82,10 +91,12 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx

// Usually should just get string value:
if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
String name = p.getText();
Object result = _enumLookup.find(name);
CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
? _getToStringLookup(ctxt) : _lookupByName;
final String name = p.getText();
Object result = lookup.find(name);
if (result == null) {
return _deserializeAltString(p, ctxt, name);
return _deserializeAltString(p, ctxt, lookup, name);
}
return result;
}
Expand All @@ -112,8 +123,14 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
return _deserializeOther(p, ctxt);
}

/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/

private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt,
String name) throws IOException
CompactStringObjectMap lookup, String name) throws IOException
{
name = name.trim();
if (name.length() == 0) {
Expand Down Expand Up @@ -143,7 +160,7 @@ private final Object _deserializeAltString(JsonParser p, DeserializationContext
}
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
throw ctxt.weirdStringException(name, _enumClass(),
"value not one of declared Enum instance names: "+_enumLookup.keys());
"value not one of declared Enum instance names: "+lookup.keys());
}
return null;
}
Expand Down Expand Up @@ -179,6 +196,22 @@ protected Class<?> _enumClass() {
return handledType();
}

protected CompactStringObjectMap _getToStringLookup(DeserializationContext ctxt)
{
CompactStringObjectMap lookup = _lookupByToString;
// note: exact locking not needed; all we care for here is to try to
// reduce contention for the initial resolution
if (lookup == null) {
synchronized (this) {
lookup = EnumResolver.constructUnsafeUsingToString(_enumClass(),
ctxt.getAnnotationIntrospector())
.constructLookup();
}
_lookupByToString = lookup;
}
return lookup;
}

/*
/**********************************************************
/* Additional helper classes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
import java.util.*;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.io.NumberInput;
Expand Down Expand Up @@ -117,7 +113,7 @@ public static StdKeyDeserializer forType(Class<?> raw)

@Override
public Object deserializeKey(String key, DeserializationContext ctxt)
throws IOException, JsonProcessingException
throws IOException
{
if (key == null) { // is this even legal call?
return null;
Expand Down Expand Up @@ -318,13 +314,21 @@ final static class EnumKD extends StdKeyDeserializer
{
private static final long serialVersionUID = 1L;

protected final EnumResolver _resolver;
protected final EnumResolver _byNameResolver;

protected final AnnotatedMethod _factory;

/**
* Lazily constructed alternative in case there is need to
* use 'toString()' method as the source.
*
* @since 2.7.3
*/
protected EnumResolver _byToStringResolver;

protected EnumKD(EnumResolver er, AnnotatedMethod factory) {
super(-1, er.getEnumClass());
_resolver = er;
_byNameResolver = er;
_factory = factory;
}

Expand All @@ -338,12 +342,27 @@ public Object _parse(String key, DeserializationContext ctxt) throws JsonMapping
ClassUtil.unwrapAndThrowAsIAE(e);
}
}
Enum<?> e = _resolver.findEnum(key);
EnumResolver res = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
? _getToStringResolver(ctxt) : _byNameResolver;
Enum<?> e = res.findEnum(key);
if ((e == null) && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
throw ctxt.weirdKeyException(_keyClass, key, "not one of values for Enum class");
throw ctxt.weirdKeyException(_keyClass, key, "not one of values excepted for Enum class: "
+res.getEnumIds());
}
return e;
}

private EnumResolver _getToStringResolver(DeserializationContext ctxt)
{
EnumResolver res = _byToStringResolver;
if (res == null) {
synchronized (this) {
res = EnumResolver.constructUnsafeUsingToString(_byNameResolver.getEnumClass(),
ctxt.getAnnotationIntrospector());
}
}
return res;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,73 +27,73 @@ public enum JsonValueFormat
DDThh:mm:ssZ in UTC time. This is the recommended form of date/
timestamp.
*/
DATE_TIME("date-time"),
DATE_TIME("date-time"),

/**
* This SHOULD be an email address.
*/
EMAIL("email"),
/**
* This SHOULD be an email address.
*/
EMAIL("email"),

/**
* This SHOULD be a host-name.
*/
HOST_NAME("host-name"),
/**
* This SHOULD be a host-name.
*/
HOST_NAME("host-name"),

/**
* This SHOULD be an ip version 4 address.
*/
IP_ADDRESS("ip-address"),

/**
* This SHOULD be an ip version 6 address.
*/
IPV6("ipv6"),

/**
* This SHOULD be a phone number (format MAY follow E.123).
*/
PHONE("phone"),

/**
* A regular expression, following the regular expression
specification from ECMA 262/Perl 5.
*/
REGEX("regex"),
/**
* This SHOULD be an ip version 4 address.
*/
IP_ADDRESS("ip-address"),

/**
* This is a CSS style definition (like "color: red; background-
color:#FFF"), based on CSS 2.1 [W3C.CR-CSS21-20070719].
*/
STYLE("style"),
/**
* This SHOULD be an ip version 6 address.
*/
IPV6("ipv6"),

/**
* This SHOULD be a time in the format of hh:mm:ss. It is
recommended that you use the "date-time" format instead of "time"
unless you need to transfer only the time part.
*/
TIME("time"),
/**
* This SHOULD be a phone number (format MAY follow E.123).
*/
PHONE("phone"),

/**
* This value SHOULD be a URI..
*/
URI("uri"),
/**
* A regular expression, following the regular expression
* specification from ECMA 262/Perl 5.
*/
REGEX("regex"),

/**
* This is a CSS style definition (like "color: red; background-
* color:#FFF"), based on CSS 2.1 [W3C.CR-CSS21-20070719].
*/
STYLE("style"),

/**
* This SHOULD be a time in the format of hh:mm:ss. It is
* recommended that you use the "date-time" format instead of "time"
* unless you need to transfer only the time part.
*/
TIME("time"),

/**
* This value SHOULD be a URI..
*/
URI("uri"),

/**
* This SHOULD be the difference, measured in
/**
* This SHOULD be the difference, measured in
milliseconds, between the specified time and midnight, 00:00 of
January 1, 1970 UTC. The value SHOULD be a number (integer or
float).
*/
UTC_MILLISEC("utc-millisec"),
;

private final String _desc;
private JsonValueFormat(String desc) {
_desc = desc;
}

@Override
@JsonValue // since 2.7
public String toString() { return _desc; }
*/
UTC_MILLISEC("utc-millisec"),
;

private final String _desc;

private JsonValueFormat(String desc) {
_desc = desc;
}

@Override
@JsonValue // since 2.7
public String toString() { return _desc; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
Expand Down Expand Up @@ -61,15 +62,25 @@ public void serializeWithType(byte[] value, JsonGenerator g, SerializerProvider
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
ObjectNode o = createSchemaNode("array", true);
ObjectNode itemSchema = createSchemaNode("string"); //binary values written as strings?
ObjectNode itemSchema = createSchemaNode("byte"); //binary values written as strings?
return o.set("items", itemSchema);
}

@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
throws JsonMappingException
{
// while logically (and within JVM) binary, gets encoded as Base64 String
visitArrayFormat(visitor, typeHint, JsonFormatTypes.STRING);
// 14-Mar-2016, tatu: while logically (and within JVM) binary, gets encoded as Base64 String,
// let's try to indicate it is array of Bytes... difficult, thanks to JSON Schema's
// lackluster listing of types
//
// TODO: for 2.8, make work either as String/base64, or array of numbers,
// with a qualifier that can be used to determine it's byte[]
if (visitor != null) {
JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
if (v2 != null) {
v2.itemsFormat(JsonFormatTypes.INTEGER);
}
}
}
}
}
Loading

0 comments on commit bb8a0b3

Please sign in to comment.