IndexReportingObjectArrayDeserializer.java
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.addthis.codec.jackson;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.util.ObjectBuffer;
/**
* Provides missing array index information from error path reporting. Based on the version from master, but
* with some additional improvements.
*/
public class IndexReportingObjectArrayDeserializer extends ObjectArrayDeserializer implements ContextualDeserializer {
public IndexReportingObjectArrayDeserializer(ArrayType arrayType,
JsonDeserializer<Object> elemDeser,
TypeDeserializer elemTypeDeser) {
super(arrayType, elemDeser, elemTypeDeser);
}
/** Overridable fluent-factory method used to create contextual instances */
@SuppressWarnings("unchecked")
public IndexReportingObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
JsonDeserializer<?> elemDeser) {
if ((elemDeser == _elementDeserializer) && (elemTypeDeser == _elementTypeDeserializer)) {
return this;
}
return new IndexReportingObjectArrayDeserializer(
_arrayType, (JsonDeserializer<Object>) elemDeser, elemTypeDeser);
}
@Override
public Object[] deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
if (!jp.isExpectedStartArrayToken()) {
// punt to super method for edge case handling, we won't need to report any indexes anyway
return super.deserialize(jp, ctxt);
}
final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
Object[] chunk = buffer.resetAndStart();
int ix = 0;
JsonToken t;
final TypeDeserializer typeDeser = _elementTypeDeserializer;
try {
while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
ix = 0;
}
// Note: must handle null explicitly here; value deserializers won't
Object value;
if (t == JsonToken.VALUE_NULL) {
value = _elementDeserializer.getNullValue();
} else if (typeDeser == null) {
value = _elementDeserializer.deserialize(jp, ctxt);
} else {
value = _elementDeserializer.deserializeWithType(jp, ctxt, typeDeser);
}
chunk[ix] = value;
ix++;
}
} catch (Exception e) {
// note: pass Object.class, not Object[].class, as we need element type for error info
// addition: add buffer.bufferedSize() since ix gets periodically reset
// addition: report _elementClass instead of hard-coded Object.class
throw JsonMappingException.wrapWithPath(e, _elementClass, ix + buffer.bufferedSize());
}
Object[] result;
if (_untyped) {
result = buffer.completeAndClearBuffer(chunk, ix);
} else {
result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
}
ctxt.returnObjectBuffer(buffer);
return result;
}
}