package com.fasterxml.jackson.databind.deser.std;
import java.io.*;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
@SuppressWarnings("serial")
public class JdkDeserializers
{
public static StdDeserializer<?>[] all()
{
return new StdDeserializer[] {
// from String types:
new StringDeserializer(),
new UUIDDeserializer(),
new URLDeserializer(),
new URIDeserializer(),
new CurrencyDeserializer(),
new PatternDeserializer(),
new LocaleDeserializer(),
new InetAddressDeserializer(),
new CharsetDeserializer(),
// other types:
// (note: AtomicInteger/Long work due to single-arg constructor;
new AtomicBooleanDeserializer(),
new ClassDeserializer(),
new StackTraceElementDeserializer()
};
}
/*
/**********************************************************
/* Deserializer implementations: from-String deserializers
/**********************************************************
*/
/**
* Note: final as performance optimization: not expected to need sub-classing;
* if sub-classing was needed could re-factor into reusable part, final
* "Impl" sub-class
*/
/*
@JacksonStdImpl
public final static class StringDeserializer
extends StdScalarDeserializer<String>
{
public StringDeserializer() { super(String.class); }
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
// 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
String text = jp.getValueAsString();
if (text != null) {
return text;
}
// [JACKSON-330]: need to gracefully handle byte[] data, as base64
JsonToken curr = jp.getCurrentToken();
if (curr == JsonToken.VALUE_EMBEDDED_OBJECT) {
Object ob = jp.getEmbeddedObject();
if (ob == null) {
return null;
}
if (ob instanceof byte[]) {
return Base64Variants.getDefaultVariant().encode((byte[]) ob, false);
}
// otherwise, try conversion using toString()...
return ob.toString();
}
throw ctxt.mappingException(_valueClass, curr);
}
// 1.6: since we can never have type info ("natural type"; String, Boolean, Integer, Double):
// (is it an error to even call this version?)
@Override
public String deserializeWithType(JsonParser jp, DeserializationContext ctxt,
TypeDeserializer typeDeserializer)
throws IOException, JsonProcessingException
{
return deserialize(jp, ctxt);
}
}
*/
public static class UUIDDeserializer
extends FromStringDeserializer<UUID>
{
public UUIDDeserializer() { super(UUID.class); }
@Override
protected UUID _deserialize(String value, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return UUID.fromString(value);
}
@Override
protected UUID _deserializeEmbedded(Object ob, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (ob instanceof byte[]) {
byte[] bytes = (byte[]) ob;
if (bytes.length != 16) {
ctxt.mappingException("Can only construct UUIDs from 16 byte arrays; got "+bytes.length+" bytes");
}
// clumsy, but should work for now...
DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
long l1 = in.readLong();
long l2 = in.readLong();
return new UUID(l1, l2);
}
super._deserializeEmbedded(ob, ctxt);
return null; // never gets here
}
}
public static class URLDeserializer
extends FromStringDeserializer<URL>
{
public URLDeserializer() { super(URL.class); }
@Override
protected URL _deserialize(String value, DeserializationContext ctxt)
throws IOException
{
return new URL(value);
}
}
public static class URIDeserializer
extends FromStringDeserializer<URI>
{
public URIDeserializer() { super(URI.class); }
@Override
protected URI _deserialize(String value, DeserializationContext ctxt)
throws IllegalArgumentException
{
return URI.create(value);
}
}
public static class CurrencyDeserializer
extends FromStringDeserializer<Currency>
{
public CurrencyDeserializer() { super(Currency.class); }
@Override
protected Currency _deserialize(String value, DeserializationContext ctxt)
throws IllegalArgumentException
{
// will throw IAE if unknown:
return Currency.getInstance(value);
}
}
public static class PatternDeserializer
extends FromStringDeserializer<Pattern>
{
public PatternDeserializer() { super(Pattern.class); }
@Override
protected Pattern _deserialize(String value, DeserializationContext ctxt)
throws IllegalArgumentException
{
// will throw IAE (or its subclass) if malformed
return Pattern.compile(value);
}
}
/**
* Kept protected as it's not meant to be extensible at this point
*/
protected static class LocaleDeserializer
extends FromStringDeserializer<Locale>
{
public LocaleDeserializer() { super(Locale.class); }
@Override
protected Locale _deserialize(String value, DeserializationContext ctxt)
throws IOException
{
int ix = value.indexOf('_');
if (ix < 0) { // single argument
return new Locale(value);
}
String first = value.substring(0, ix);
value = value.substring(ix+1);
ix = value.indexOf('_');
if (ix < 0) { // two pieces
return new Locale(first, value);
}
String second = value.substring(0, ix);
return new Locale(first, second, value.substring(ix+1));
}
}
/**
* As per [JACKSON-484], also need special handling for InetAddress...
*/
protected static class InetAddressDeserializer
extends FromStringDeserializer<InetAddress>
{
public InetAddressDeserializer() { super(InetAddress.class); }
@Override
protected InetAddress _deserialize(String value, DeserializationContext ctxt)
throws IOException
{
return InetAddress.getByName(value);
}
}
// [JACKSON-789]
protected static class CharsetDeserializer
extends FromStringDeserializer<Charset>
{
public CharsetDeserializer() { super(Charset.class); }
@Override
protected Charset _deserialize(String value, DeserializationContext ctxt)
throws IOException
{
return Charset.forName(value);
}
}
/*
/**********************************************************
/* AtomicXxx types
/**********************************************************
*/
public static class AtomicReferenceDeserializer
extends StdScalarDeserializer<AtomicReference<?>>
implements ContextualDeserializer
{
/**
* Type of value that we reference
*/
protected final JavaType _referencedType;
protected final JsonDeserializer<?> _valueDeserializer;
/**
* @param referencedType Parameterization of this reference
*/
public AtomicReferenceDeserializer(JavaType referencedType) {
this(referencedType, null);
}
public AtomicReferenceDeserializer(JavaType referencedType,
JsonDeserializer<?> deser)
{
super(AtomicReference.class);
_referencedType = referencedType;
_valueDeserializer = deser;
}
@Override
public AtomicReference<?> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return new AtomicReference<Object>(_valueDeserializer.deserialize(jp, ctxt));
}
// @Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
JsonDeserializer<?> deser = _valueDeserializer;
if (deser != null) {
return this;
}
return new AtomicReferenceDeserializer(_referencedType,
ctxt.findContextualValueDeserializer(_referencedType, property));
}
}
public static class AtomicBooleanDeserializer
extends StdScalarDeserializer<AtomicBoolean>
{
public AtomicBooleanDeserializer() { super(AtomicBoolean.class); }
@Override
public AtomicBoolean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
// 16-Dec-2010, tatu: Should we actually convert null to null AtomicBoolean?
return new AtomicBoolean(_parseBooleanPrimitive(jp, ctxt));
}
}
/*
/**********************************************************
/* Deserializers for other JDK types
/**********************************************************
*/
public static class StackTraceElementDeserializer
extends StdScalarDeserializer<StackTraceElement>
{
public StackTraceElementDeserializer() { super(StackTraceElement.class); }
@Override
public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
JsonToken t = jp.getCurrentToken();
// Must get an Object
if (t == JsonToken.START_OBJECT) {
String className = "", methodName = "", fileName = "";
int lineNumber = -1;
while ((t = jp.nextValue()) != JsonToken.END_OBJECT) {
String propName = jp.getCurrentName();
if ("className".equals(propName)) {
className = jp.getText();
} else if ("fileName".equals(propName)) {
fileName = jp.getText();
} else if ("lineNumber".equals(propName)) {
if (t.isNumeric()) {
lineNumber = jp.getIntValue();
} else {
throw JsonMappingException.from(jp, "Non-numeric token ("+t+") for property 'lineNumber'");
}
} else if ("methodName".equals(propName)) {
methodName = jp.getText();
} else if ("nativeMethod".equals(propName)) {
// no setter, not passed via constructor: ignore
} else {
handleUnknownProperty(jp, ctxt, _valueClass, propName);
}
}
return new StackTraceElement(className, methodName, fileName, lineNumber);
}
throw ctxt.mappingException(_valueClass, t);
}
}
}