com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
package com.fasterxml.jackson.databind.deser.std;
import java.io.IOException;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.node.*;
/**
* Deserializer that can build instances of {@link JsonNode} from any
* JSON content, using appropriate {@link JsonNode} type.
*/
@SuppressWarnings("serial")
public class JsonNodeDeserializer
extends BaseNodeDeserializer
{
/**
* Singleton instance of generic deserializer for {@link JsonNode}.
* Only used for types other than JSON Object and Array.
*/
private final static JsonNodeDeserializer instance = new JsonNodeDeserializer();
protected JsonNodeDeserializer() { super(JsonNode.class); }
/**
* Factory method for accessing deserializer for specific node type
*/
public static JsonDeserializer extends JsonNode> getDeserializer(Class> nodeClass)
{
if (nodeClass == ObjectNode.class) {
return ObjectDeserializer.getInstance();
}
if (nodeClass == ArrayNode.class) {
return ArrayDeserializer.getInstance();
}
// For others, generic one works fine
return instance;
}
/*
/**********************************************************
/* Actual deserializer implementations
/**********************************************************
*/
@Override
public JsonNode getNullValue() {
return NullNode.getInstance();
}
/**
* Implementation that will produce types of any JSON nodes; not just one
* deserializer is registered to handle (in case of more specialized handler).
* Overridden by typed sub-classes for more thorough checking
*/
@Override
public JsonNode deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
switch (jp.getCurrentToken()) {
case START_OBJECT:
return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
case START_ARRAY:
return deserializeArray(jp, ctxt, ctxt.getNodeFactory());
default:
return deserializeAny(jp, ctxt, ctxt.getNodeFactory());
}
}
/*
/**********************************************************
/* Specific instances for more accurate types
/**********************************************************
*/
final static class ObjectDeserializer
extends BaseNodeDeserializer
{
private static final long serialVersionUID = 1L;
protected final static ObjectDeserializer _instance = new ObjectDeserializer();
protected ObjectDeserializer() { super(ObjectNode.class); }
public static ObjectDeserializer getInstance() { return _instance; }
@Override
public ObjectNode deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
jp.nextToken();
return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
}
if (jp.getCurrentToken() == JsonToken.FIELD_NAME) {
return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
}
throw ctxt.mappingException(ObjectNode.class);
}
}
final static class ArrayDeserializer
extends BaseNodeDeserializer
{
private static final long serialVersionUID = 1L;
protected final static ArrayDeserializer _instance = new ArrayDeserializer();
protected ArrayDeserializer() { super(ArrayNode.class); }
public static ArrayDeserializer getInstance() { return _instance; }
@Override
public ArrayNode deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.isExpectedStartArrayToken()) {
return deserializeArray(jp, ctxt, ctxt.getNodeFactory());
}
throw ctxt.mappingException(ArrayNode.class);
}
}
}
/**
* Base class for all actual {@link JsonNode} deserializer
* implementations
*/
@SuppressWarnings("serial")
abstract class BaseNodeDeserializer
extends StdDeserializer
{
public BaseNodeDeserializer(Class vc)
{
super(vc);
}
@Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
/* Output can be as JSON Object, Array or scalar: no way to know
* a priori. So:
*/
return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
}
/*
/**********************************************************
/* Overridable methods
/**********************************************************
*/
protected void _reportProblem(JsonParser jp, String msg)
throws JsonMappingException
{
throw new JsonMappingException(msg, jp.getTokenLocation());
}
/**
*
* @deprecated Since 2.3, use the overloaded variant
*/
@Deprecated
protected void _handleDuplicateField(String fieldName, ObjectNode objectNode,
JsonNode oldValue, JsonNode newValue)
throws JsonProcessingException
{
// By default, we don't do anything
;
}
/**
* Method called when there is a duplicate value for a field.
* By default we don't care, and the last value is used.
* Can be overridden to provide alternate handling, such as throwing
* an exception, or choosing different strategy for combining values
* or choosing which one to keep.
*
* @param fieldName Name of the field for which duplicate value was found
* @param objectNode Object node that contains values
* @param oldValue Value that existed for the object node before newValue
* was added
* @param newValue Newly added value just added to the object node
*/
protected void _handleDuplicateField(JsonParser jp, DeserializationContext ctxt,
JsonNodeFactory nodeFactory,
String fieldName, ObjectNode objectNode,
JsonNode oldValue, JsonNode newValue)
throws JsonProcessingException
{
// [Issue#237]: Report an error if asked to do so:
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) {
_reportProblem(jp, "Duplicate field '"+fieldName+"' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled");
}
// Backwards-compatibility; call in case it's overloaded
_handleDuplicateField(fieldName, objectNode, oldValue, newValue);
}
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
protected final ObjectNode deserializeObject(JsonParser jp, DeserializationContext ctxt,
final JsonNodeFactory nodeFactory)
throws IOException, JsonProcessingException
{
ObjectNode node = nodeFactory.objectNode();
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.START_OBJECT) {
t = jp.nextToken();
}
for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
String fieldName = jp.getCurrentName();
JsonNode value;
switch (jp.nextToken()) {
case START_OBJECT:
value = deserializeObject(jp, ctxt, nodeFactory);
break;
case START_ARRAY:
value = deserializeArray(jp, ctxt, nodeFactory);
break;
case VALUE_STRING:
value = nodeFactory.textNode(jp.getText());
break;
default:
value = deserializeAny(jp, ctxt, nodeFactory);
}
JsonNode old = node.replace(fieldName, value);
if (old != null) {
_handleDuplicateField(jp, ctxt, nodeFactory,
fieldName, node, old, value);
}
}
return node;
}
protected final ArrayNode deserializeArray(JsonParser jp, DeserializationContext ctxt,
final JsonNodeFactory nodeFactory)
throws IOException, JsonProcessingException
{
ArrayNode node = nodeFactory.arrayNode();
while (true) {
JsonToken t = jp.nextToken();
if (t == null) {
throw ctxt.mappingException("Unexpected end-of-input when binding data into ArrayNode");
}
switch (t) {
case START_OBJECT:
node.add(deserializeObject(jp, ctxt, nodeFactory));
break;
case START_ARRAY:
node.add(deserializeArray(jp, ctxt, nodeFactory));
break;
case END_ARRAY:
return node;
case VALUE_STRING:
node.add(nodeFactory.textNode(jp.getText()));
break;
default:
node.add(deserializeAny(jp, ctxt, nodeFactory));
break;
}
}
}
protected final JsonNode deserializeAny(JsonParser jp, DeserializationContext ctxt,
final JsonNodeFactory nodeFactory)
throws IOException, JsonProcessingException
{
switch (jp.getCurrentToken()) {
case START_OBJECT:
case END_OBJECT: // for empty JSON Objects we may point to this
return deserializeObject(jp, ctxt, nodeFactory);
case START_ARRAY:
return deserializeArray(jp, ctxt, nodeFactory);
case FIELD_NAME:
return deserializeObject(jp, ctxt, nodeFactory);
case VALUE_EMBEDDED_OBJECT:
// [JACKSON-796]
{
Object ob = jp.getEmbeddedObject();
if (ob == null) { // should this occur?
return nodeFactory.nullNode();
}
Class> type = ob.getClass();
if (type == byte[].class) { // most common special case
return nodeFactory.binaryNode((byte[]) ob);
}
if (JsonNode.class.isAssignableFrom(type)) {
// [Issue#433]: but could also be a JsonNode hiding in there!
return (JsonNode) ob;
}
// any other special handling needed?
return nodeFactory.pojoNode(ob);
}
case VALUE_STRING:
return nodeFactory.textNode(jp.getText());
case VALUE_NUMBER_INT:
{
JsonParser.NumberType nt = jp.getNumberType();
if (nt == JsonParser.NumberType.BIG_INTEGER
|| ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
return nodeFactory.numberNode(jp.getBigIntegerValue());
}
if (nt == JsonParser.NumberType.INT) {
return nodeFactory.numberNode(jp.getIntValue());
}
return nodeFactory.numberNode(jp.getLongValue());
}
case VALUE_NUMBER_FLOAT:
{
JsonParser.NumberType nt = jp.getNumberType();
if (nt == JsonParser.NumberType.BIG_DECIMAL
|| ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
return nodeFactory.numberNode(jp.getDecimalValue());
}
return nodeFactory.numberNode(jp.getDoubleValue());
}
case VALUE_TRUE:
return nodeFactory.booleanNode(true);
case VALUE_FALSE:
return nodeFactory.booleanNode(false);
case VALUE_NULL:
return nodeFactory.nullNode();
// These states can not be mapped; input stream is
// off by an event or two
//case END_OBJECT:
//case END_ARRAY:
default:
throw ctxt.mappingException(handledType());
}
}
}