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.
*/
public class JsonNodeDeserializer
extends BaseNodeDeserializer<JsonNode>
{
/**
* 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
/**********************************************************
*/
/**
* 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<ObjectNode>
{
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<ArrayNode>
{
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
*/
abstract class BaseNodeDeserializer<N extends JsonNode>
extends StdDeserializer<N>
{
public BaseNodeDeserializer(Class<N> nodeClass)
{
super(nodeClass);
}
@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());
}
/**
* 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(String fieldName, ObjectNode objectNode,
JsonNode oldValue, JsonNode newValue)
throws JsonProcessingException
{
// By default, we don't do anything
;
}
/*
/**********************************************************
/* 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(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:
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);
}
// 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(getValueClass());
}
}
}