Skip to content

Commit

Permalink
Yet more refactoring to figure out coercion for empty String
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed May 31, 2020
1 parent 5cfeba0 commit 7ba9ac5
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1529,7 +1529,7 @@ public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt)
/**
* @since 2.9
*/
private final JsonDeserializer<Object> _delegateDeserializer() {
protected final JsonDeserializer<Object> _delegateDeserializer() {
JsonDeserializer<Object> deser = _delegateDeserializer;
if (deser == null) {
deser = _arrayDelegateDeserializer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,10 @@ public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext
{
// Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
JsonToken t = p.currentToken();
if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
// String may be ok however:
// slightly redundant (since String was passed above), but
return _deserializeFromEmpty(p, ctxt);
}
if (t == JsonToken.START_OBJECT) {
t = p.nextToken();
} else if (t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
return _deserializeFromEmpty(p, ctxt);
}
if (t != JsonToken.FIELD_NAME) {
if (t == JsonToken.END_OBJECT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected boolean isDefaultDeserializer(JsonDeserializer<?> deserializer) {
protected boolean isDefaultKeyDeserializer(KeyDeserializer keyDeser) {
return ClassUtil.isJacksonStdImpl(keyDeser);
}

/*
/**********************************************************
/* Partial JsonDeserializer implementation
Expand All @@ -160,6 +160,101 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
return typeDeserializer.deserializeTypedFromAny(p, ctxt);
}

/*
/**********************************************************
/* High-level handling of secondary input shapes (with
/* possible coercion)
/**********************************************************
*/

/**
* Helper method that allows easy support for array-related {@link DeserializationFeature}s
* `ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT` and `UNWRAP_SINGLE_VALUE_ARRAYS`: checks for either
* empty array, or single-value array-wrapped value (respectively), and either reports
* an exception (if no match, or feature(s) not enabled), or returns appropriate
* result value.
*<p>
* This method should NOT be called if Array representation is explicitly supported
* for type: it should only be called in case it is otherwise unrecognized.
*<p>
* NOTE: in case of unwrapped single element, will handle actual decoding
* by calling {@link #_deserializeWrappedValue}, which by default calls
* {@link #deserialize(JsonParser, DeserializationContext)}.
*
* @since 2.9
*/
protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t;
if (ctxt.hasSomeOfFeatures(F_MASK_ACCEPT_ARRAYS)) {
t = p.nextToken();
if (t == JsonToken.END_ARRAY) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
return getNullValue(ctxt);
}
}
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
final T parsed = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
} else {
t = p.currentToken();
}
@SuppressWarnings("unchecked")
T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, null);
return result;
}

/**
* Helper method that may be used to support fallback for Empty String / Empty Array
* non-standard representations; usually for things serialized as JSON Objects.
*
* @since 2.5
*/
@SuppressWarnings("unchecked")
protected T _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt)
throws IOException
{
JsonToken t = p.currentToken();
if (t == JsonToken.START_ARRAY) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
t = p.nextToken();
if (t == JsonToken.END_ARRAY) {
return null;
}
return (T) ctxt.handleUnexpectedToken(handledType(), p);
}
}
return (T) ctxt.handleUnexpectedToken(handledType(), p);
}

/**
* Helper called to support {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS}:
* default implementation simply calls
* {@link #deserialize(JsonParser, DeserializationContext)},
* but handling may be overridden.
*
* @since 2.9
*/
protected T _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt) throws IOException
{
// 23-Mar-2017, tatu: Let's specifically block recursive resolution to avoid
// either supporting nested arrays, or to cause infinite looping.
if (p.hasToken(JsonToken.START_ARRAY)) {
String msg = String.format(
"Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s",
ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY,
"DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS");
@SuppressWarnings("unchecked")
T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, msg);
return result;
}
return (T) deserialize(p, ctxt);
}

/*
/**********************************************************
/* Helper methods for sub-classes, parsing: while mostly
Expand Down Expand Up @@ -616,36 +711,6 @@ protected final String _parseString(JsonParser p, DeserializationContext ctxt) t
return (String) ctxt.handleUnexpectedToken(String.class, p);
}

/**
* Helper method that may be used to support fallback for Empty String / Empty Array
* non-standard representations; usually for things serialized as JSON Objects.
*
* @since 2.5
*/
@SuppressWarnings("unchecked")
protected T _deserializeFromEmpty(JsonParser p, DeserializationContext ctxt)
throws IOException
{
JsonToken t = p.currentToken();
if (t == JsonToken.START_ARRAY) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
t = p.nextToken();
if (t == JsonToken.END_ARRAY) {
return null;
}
return (T) ctxt.handleUnexpectedToken(handledType(), p);
}
} else if (t == JsonToken.VALUE_STRING) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
String str = p.getText().trim();
if (str.isEmpty()) {
return null;
}
}
}
return (T) ctxt.handleUnexpectedToken(handledType(), p);
}

/**
* Helper method called to determine if we are seeing String value of
* "null", and, further, that it should be coerced to null just like
Expand Down Expand Up @@ -674,78 +739,6 @@ protected final boolean _isPosInf(String text) {

protected final boolean _isNaN(String text) { return "NaN".equals(text); }

/*
/**********************************************************
/* Helper methods for sub-classes regarding decoding from
/* alternate representations
/**********************************************************
*/

/**
* Helper method that allows easy support for array-related {@link DeserializationFeature}s
* `ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT` and `UNWRAP_SINGLE_VALUE_ARRAYS`: checks for either
* empty array, or single-value array-wrapped value (respectively), and either reports
* an exception (if no match, or feature(s) not enabled), or returns appropriate
* result value.
*<p>
* This method should NOT be called if Array representation is explicitly supported
* for type: it should only be called in case it is otherwise unrecognized.
*<p>
* NOTE: in case of unwrapped single element, will handle actual decoding
* by calling {@link #_deserializeWrappedValue}, which by default calls
* {@link #deserialize(JsonParser, DeserializationContext)}.
*
* @since 2.9
*/
protected T _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException
{
JsonToken t;
if (ctxt.hasSomeOfFeatures(F_MASK_ACCEPT_ARRAYS)) {
t = p.nextToken();
if (t == JsonToken.END_ARRAY) {
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)) {
return getNullValue(ctxt);
}
}
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
final T parsed = deserialize(p, ctxt);
if (p.nextToken() != JsonToken.END_ARRAY) {
handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
} else {
t = p.currentToken();
}
@SuppressWarnings("unchecked")
T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, null);
return result;
}

/**
* Helper called to support {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS}:
* default implementation simply calls
* {@link #deserialize(JsonParser, DeserializationContext)},
* but handling may be overridden.
*
* @since 2.9
*/
protected T _deserializeWrappedValue(JsonParser p, DeserializationContext ctxt) throws IOException
{
// 23-Mar-2017, tatu: Let's specifically block recursive resolution to avoid
// either supporting nested arrays, or to cause infinite looping.
if (p.hasToken(JsonToken.START_ARRAY)) {
String msg = String.format(
"Cannot deserialize instance of %s out of %s token: nested Arrays not allowed with %s",
ClassUtil.nameOf(_valueClass), JsonToken.START_ARRAY,
"DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS");
@SuppressWarnings("unchecked")
T result = (T) ctxt.handleUnexpectedToken(getValueType(ctxt), p.currentToken(), p, msg);
return result;
}
return (T) deserialize(p, ctxt);
}

/*
/****************************************************
/* Helper methods for sub-classes, coercions
Expand Down

0 comments on commit 7ba9ac5

Please sign in to comment.