All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.databind.deser.std.FromStringDeserializer Maven / Gradle / Ivy

There is a newer version: 2.17.0
Show newest version
package com.fasterxml.jackson.databind.deser.std;

import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Currency;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.VersionUtil;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
 * Base class for simple deserializers that serialize values from String
 * representation: this includes JSON Strings and other Scalar values that
 * can be coerced into text, like Numbers and Booleans).
 * Simple JSON String values are trimmed using {@link java.lang.String#trim}.
 * Partial deserializer implementation will try to first access current token as
 * a String, calls {@link #_deserialize(String,DeserializationContext)} and
 * returns return value.
 * If this does not work (current token not a simple scalar type), attempts
 * are made so that:
 *
    *
  • Embedded values ({@link JsonToken#VALUE_EMBEDDED_OBJECT}) are returned as-is * if they are of compatible type *
  • *
  • Arrays may be "unwrapped" if (and only if) {@link DeserializationFeature#UNWRAP_SINGLE_VALUE_ARRAYS} * is enabled, and array contains just a single scalar value that can be deserialized * (for example, JSON Array with single JSON String element). *
  • *
*

* Special handling includes: *

    *
  • Null values ({@link JsonToken#VALUE_NULL}) are handled by returning value * returned by {@link JsonDeserializer#getNullValue(DeserializationContext)}: default * implementation simply returns Java `null` but this may be overridden. *
  • *
  • Empty String (after trimming) will result in {@link #_deserializeFromEmptyString} * getting called, and return value being returned as deserialization: default implementation * simply returns `null`. *
  • *
*/ @SuppressWarnings("serial") public abstract class FromStringDeserializer extends StdScalarDeserializer { public static Class[] types() { return new Class[] { File.class, URL.class, URI.class, Class.class, JavaType.class, Currency.class, Pattern.class, Locale.class, Charset.class, TimeZone.class, InetAddress.class, InetSocketAddress.class, StringBuilder.class, }; } /* /********************************************************** /* Deserializer implementations /********************************************************** */ protected FromStringDeserializer(Class vc) { super(vc); } /** * Factory method for trying to find a deserializer for one of supported * types that have simple from-String serialization. */ public static Std findDeserializer(Class rawType) { int kind = 0; if (rawType == File.class) { kind = Std.STD_FILE; } else if (rawType == URL.class) { kind = Std.STD_URL; } else if (rawType == URI.class) { kind = Std.STD_URI; } else if (rawType == Class.class) { kind = Std.STD_CLASS; } else if (rawType == JavaType.class) { kind = Std.STD_JAVA_TYPE; } else if (rawType == Currency.class) { kind = Std.STD_CURRENCY; } else if (rawType == Pattern.class) { kind = Std.STD_PATTERN; } else if (rawType == Locale.class) { kind = Std.STD_LOCALE; } else if (rawType == Charset.class) { kind = Std.STD_CHARSET; } else if (rawType == TimeZone.class) { kind = Std.STD_TIME_ZONE; } else if (rawType == InetAddress.class) { kind = Std.STD_INET_ADDRESS; } else if (rawType == InetSocketAddress.class) { kind = Std.STD_INET_SOCKET_ADDRESS; } else if (rawType == StringBuilder.class) { kind = Std.STD_STRING_BUILDER; } else { return null; } return new Std(rawType, kind); } /* /********************************************************** /* Deserializer implementations /********************************************************** */ @SuppressWarnings("unchecked") @Override public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // Let's get textual value, possibly via coercion from other scalar types String text = p.getValueAsString(); if (text != null) { // has String representation if (text.length() == 0 || (text = text.trim()).length() == 0) { // Usually should become null; but not always return _deserializeFromEmptyString(); } Exception cause = null; try { // 19-May-2017, tatu: Used to require non-null result (assuming `null` // indicated error; but that seems wrong. Should be able to return // `null` as value. return _deserialize(text, ctxt); } catch (IllegalArgumentException | MalformedURLException e) { cause = e; } // note: `cause` can't be null String msg = "not a valid textual representation"; String m2 = cause.getMessage(); if (m2 != null) { msg = msg + ", problem: "+m2; } // 05-May-2016, tatu: Unlike most usage, this seems legit, so... JsonMappingException e = ctxt.weirdStringException(text, _valueClass, msg); e.initCause(cause); throw e; // nothing to do here, yet? We'll fail anyway } JsonToken t = p.getCurrentToken(); // [databind#381] if (t == JsonToken.START_ARRAY) { return _deserializeFromArray(p, ctxt); } if (t == JsonToken.VALUE_EMBEDDED_OBJECT) { // Trivial cases; null to null, instance of type itself returned as is Object ob = p.getEmbeddedObject(); if (ob == null) { return null; } if (_valueClass.isAssignableFrom(ob.getClass())) { return (T) ob; } return _deserializeEmbedded(ob, ctxt); } return (T) ctxt.handleUnexpectedToken(_valueClass, p); } protected abstract T _deserialize(String value, DeserializationContext ctxt) throws IOException; protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt) throws IOException { // default impl: error out ctxt.reportInputMismatch(this, "Don't know how to convert embedded Object of type %s into %s", ob.getClass().getName(), _valueClass.getName()); return null; } protected T _deserializeFromEmptyString() throws IOException { return null; } /* /********************************************************** /* A general-purpose implementation /********************************************************** */ /** * "Chameleon" deserializer that works on simple types that are deserialized * from a simple String. * * @since 2.4 */ public static class Std extends FromStringDeserializer { private static final long serialVersionUID = 1; public final static int STD_FILE = 1; public final static int STD_URL = 2; public final static int STD_URI = 3; public final static int STD_CLASS = 4; public final static int STD_JAVA_TYPE = 5; public final static int STD_CURRENCY = 6; public final static int STD_PATTERN = 7; public final static int STD_LOCALE = 8; public final static int STD_CHARSET = 9; public final static int STD_TIME_ZONE = 10; public final static int STD_INET_ADDRESS = 11; public final static int STD_INET_SOCKET_ADDRESS = 12; public final static int STD_STRING_BUILDER = 13; protected final int _kind; protected Std(Class valueType, int kind) { super(valueType); _kind = kind; } @Override protected Object _deserialize(String value, DeserializationContext ctxt) throws IOException { switch (_kind) { case STD_FILE: return new File(value); case STD_URL: return new URL(value); case STD_URI: return URI.create(value); case STD_CLASS: try { return ctxt.findClass(value); } catch (Exception e) { return ctxt.handleInstantiationProblem(_valueClass, value, ClassUtil.getRootCause(e)); } case STD_JAVA_TYPE: return ctxt.getTypeFactory().constructFromCanonical(value); case STD_CURRENCY: // will throw IAE if unknown: return Currency.getInstance(value); case STD_PATTERN: // will throw IAE (or its subclass) if malformed return Pattern.compile(value); case STD_LOCALE: { int ix = _firstHyphenOrUnderscore(value); if (ix < 0) { // single argument return new Locale(value); } String first = value.substring(0, ix); value = value.substring(ix+1); ix = _firstHyphenOrUnderscore(value); 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)); } case STD_CHARSET: return Charset.forName(value); case STD_TIME_ZONE: return TimeZone.getTimeZone(value); case STD_INET_ADDRESS: return InetAddress.getByName(value); case STD_INET_SOCKET_ADDRESS: if (value.startsWith("[")) { // bracketed IPv6 (with port number) int i = value.lastIndexOf(']'); if (i == -1) { throw new InvalidFormatException(ctxt.getParser(), "Bracketed IPv6 address must contain closing bracket", value, InetSocketAddress.class); } int j = value.indexOf(':', i); int port = j > -1 ? Integer.parseInt(value.substring(j + 1)) : 0; return new InetSocketAddress(value.substring(0, i + 1), port); } int ix = value.indexOf(':'); if (ix >= 0 && value.indexOf(':', ix + 1) < 0) { // host:port int port = Integer.parseInt(value.substring(ix+1)); return new InetSocketAddress(value.substring(0, ix), port); } // host or unbracketed IPv6, without port number return new InetSocketAddress(value, 0); case STD_STRING_BUILDER: return new StringBuilder(value); } VersionUtil.throwInternal(); return null; } @Override protected Object _deserializeFromEmptyString() throws IOException { // As per [databind#398], URI requires special handling if (_kind == STD_URI) { return URI.create(""); } // As per [databind#1123], Locale too if (_kind == STD_LOCALE) { return Locale.ROOT; } if (_kind == STD_STRING_BUILDER) { return new StringBuilder(); } return super._deserializeFromEmptyString(); } protected int _firstHyphenOrUnderscore(String str) { for (int i = 0, end = str.length(); i < end; ++i) { char c = str.charAt(i); if (c == '_' || c == '-') { return i; } } return -1; } } }