/* * Copyright 1999-2101 Alibaba Group. * * 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.alibaba.fastjson.util; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.math.BigDecimal; import java.math.BigInteger; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.parser.deserializer.FieldDeserializer; /** * @author wenshao<szujobs@hotmail.com> */ public class TypeUtils { public static final String castToString(Object value) { if (value == null) { return null; } return value.toString(); } public static final Byte castToByte(Object value) { if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).byteValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } return Byte.parseByte(strVal); } throw new JSONException("can not cast to byte, value : " + value); } public static final Character castToChar(Object value) { if (value == null) { return null; } if (value instanceof Character) { return (Character) value; } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } if (strVal.length() != 1) { throw new JSONException("can not cast to byte, value : " + value); } return strVal.charAt(0); } throw new JSONException("can not cast to byte, value : " + value); } public static final Short castToShort(Object value) { if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).shortValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } return Short.parseShort(strVal); } throw new JSONException("can not cast to short, value : " + value); } public static final BigDecimal castToBigDecimal(Object value) { if (value == null) { return null; } if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof BigInteger) { return new BigDecimal((BigInteger) value); } String strVal = value.toString(); if (strVal.length() == 0) { return null; } return new BigDecimal(strVal); } public static final BigInteger castToBigInteger(Object value) { if (value == null) { return null; } if (value instanceof BigInteger) { return (BigInteger) value; } if (value instanceof Float || value instanceof Double) { return BigInteger.valueOf(((Number) value).longValue()); } String strVal = value.toString(); if (strVal.length() == 0) { return null; } return new BigInteger(strVal); } public static final Float castToFloat(Object value) { if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).floatValue(); } if (value instanceof String) { String strVal = value.toString(); if (strVal.length() == 0) { return null; } return Float.parseFloat(strVal); } throw new JSONException("can not cast to float, value : " + value); } public static final Double castToDouble(Object value) { if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).doubleValue(); } if (value instanceof String) { String strVal = value.toString(); if (strVal.length() == 0) { return null; } return Double.parseDouble(strVal); } throw new JSONException("can not cast to double, value : " + value); } public static final Date castToDate(Object value) { if (value == null) { return null; } if (value instanceof Calendar) { return ((Calendar) value).getTime(); } if (value instanceof Date) { return (Date) value; } long longValue = 0; if (value instanceof Number) { longValue = ((Number) value).longValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.indexOf('-') != -1) { String format; if (strVal.length() == JSON.DEFFAULT_DATE_FORMAT.length()) { format = JSON.DEFFAULT_DATE_FORMAT; } else if (strVal.length() == 10) { format = "yyyy-MM-dd"; } else if (strVal.length() == "yyyy-MM-dd HH:mm:ss".length()) { format = "yyyy-MM-dd HH:mm:ss"; } else { format = "yyyy-MM-dd HH:mm:ss.SSS"; } SimpleDateFormat dateFormat = new SimpleDateFormat(format); try { return (Date) dateFormat.parse(strVal); } catch (ParseException e) { throw new JSONException("can not cast to Date, value : " + strVal); } } if (strVal.length() == 0) { return null; } longValue = Long.parseLong(strVal); } if (longValue <= 0) { throw new JSONException("can not cast to Date, value : " + value); } return new Date(longValue); } public static final java.sql.Date castToSqlDate(Object value) { if (value == null) { return null; } if (value instanceof Calendar) { return new java.sql.Date(((Calendar) value).getTimeInMillis()); } if (value instanceof java.sql.Date) { return (java.sql.Date) value; } if (value instanceof java.util.Date) { return new java.sql.Date(((java.util.Date) value).getTime()); } long longValue = 0; if (value instanceof Number) { longValue = ((Number) value).longValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } longValue = Long.parseLong(strVal); } if (longValue <= 0) { throw new JSONException("can not cast to Date, value : " + value); } return new java.sql.Date(longValue); } public static final java.sql.Timestamp castToTimestamp(Object value) { if (value == null) { return null; } if (value instanceof Calendar) { return new java.sql.Timestamp(((Calendar) value).getTimeInMillis()); } if (value instanceof java.sql.Timestamp) { return (java.sql.Timestamp) value; } if (value instanceof java.util.Date) { return new java.sql.Timestamp(((java.util.Date) value).getTime()); } long longValue = 0; if (value instanceof Number) { longValue = ((Number) value).longValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } longValue = Long.parseLong(strVal); } if (longValue <= 0) { throw new JSONException("can not cast to Date, value : " + value); } return new java.sql.Timestamp(longValue); } public static final Long castToLong(Object value) { if (value == null) { return null; } if (value instanceof Number) { return ((Number) value).longValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } return Long.parseLong(strVal); } throw new JSONException("can not cast to long, value : " + value); } public static final Integer castToInt(Object value) { if (value == null) { return null; } if (value instanceof Integer) { return (Integer) value; } if (value instanceof Number) { return ((Number) value).intValue(); } if (value instanceof String) { String strVal = (String) value; if (strVal.length() == 0) { return null; } return Integer.parseInt(strVal); } throw new JSONException("can not cast to int, value : " + value); } public static final byte[] castToBytes(Object value) { if (value instanceof byte[]) { return (byte[]) value; } if (value instanceof String) { return Base64.decodeFast((String) value); } throw new JSONException("can not cast to int, value : " + value); } public static final Boolean castToBoolean(Object value) { if (value == null) { return null; } if (value instanceof Boolean) { return (Boolean) value; } if (value instanceof Number) { return ((Number) value).intValue() == 1; } if (value instanceof String) { String str = (String) value; if (str.length() == 0) { return null; } if ("true".equals(str)) { return Boolean.TRUE; } if ("false".equals(str)) { return Boolean.FALSE; } if ("1".equals(str)) { return Boolean.TRUE; } } throw new JSONException("can not cast to int, value : " + value); } public static final <T> T castToJavaBean(Object obj, Class<T> clazz) { return cast(obj, clazz, ParserConfig.getGlobalInstance()); } @SuppressWarnings({"unchecked", "rawtypes"}) public static final <T> T cast(Object obj, Class<T> clazz, ParserConfig mapping) { if (obj == null) { return null; } if (clazz == null) { throw new IllegalArgumentException("clazz is null"); } if (clazz == obj.getClass()) { return (T) obj; } if (obj instanceof Map) { if (clazz == Map.class) { return (T) obj; } return castToJavaBean((Map<String, Object>) obj, clazz, mapping); } if (clazz.isArray()) { if (obj instanceof Collection) { Collection collection = (Collection) obj; int index = 0; Object array = Array.newInstance(clazz.getComponentType(), collection.size()); for (Object item : collection) { Object value = cast(item, clazz.getComponentType(), mapping); Array.set(array, index, value); index++; } return (T) array; } } if (clazz.isAssignableFrom(obj.getClass())) { return (T) obj; } if (clazz == boolean.class || clazz == Boolean.class) { return (T) castToBoolean(obj); } if (clazz == byte.class || clazz == Byte.class) { return (T) castToByte(obj); } // if (clazz == char.class || clazz == Character.class) { // return (T) castToCharacter(obj); // } if (clazz == short.class || clazz == Short.class) { return (T) castToShort(obj); } if (clazz == int.class || clazz == Integer.class) { return (T) castToInt(obj); } if (clazz == long.class || clazz == Long.class) { return (T) castToLong(obj); } if (clazz == float.class || clazz == Float.class) { return (T) castToFloat(obj); } if (clazz == double.class || clazz == Double.class) { return (T) castToDouble(obj); } if (clazz == String.class) { return (T) castToString(obj); } if (clazz == BigDecimal.class) { return (T) castToBigDecimal(obj); } if (clazz == BigInteger.class) { return (T) castToBigInteger(obj); } if (clazz == Date.class) { return (T) castToDate(obj); } if (clazz == java.sql.Date.class) { return (T) castToSqlDate(obj); } if (clazz == java.sql.Timestamp.class) { return (T) castToTimestamp(obj); } if (clazz.isEnum()) { return (T) castToEnum(obj, clazz, mapping); } if (Calendar.class.isAssignableFrom(clazz)) { Date date = castToDate(obj); Calendar calendar; if (clazz == Calendar.class) { calendar = Calendar.getInstance(); } else { try { calendar = (Calendar) clazz.newInstance(); } catch (Exception e) { throw new JSONException("can not cast to : " + clazz.getName(), e); } } calendar.setTime(date); return (T) calendar; } if (obj instanceof String) { String strVal = (String) obj; if (strVal.length() == 0) { return null; } } throw new JSONException("can not cast to : " + clazz.getName()); } @SuppressWarnings({"unchecked", "rawtypes"}) public static final <T> T castToEnum(Object obj, Class<T> clazz, ParserConfig mapping) { try { if (obj instanceof String) { String name = (String) obj; if (name.length() == 0) { return null; } return (T) Enum.valueOf((Class<? extends Enum>) clazz, name); } if (obj instanceof Number) { int ordinal = ((Number) obj).intValue(); Method method = clazz.getMethod("values"); Object[] values = (Object[]) method.invoke(null); for (Object value : values) { Enum e = (Enum) value; if (e.ordinal() == ordinal) { return (T) e; } } } } catch (Exception ex) { throw new JSONException("can not cast to : " + clazz.getName(), ex); } throw new JSONException("can not cast to : " + clazz.getName()); } @SuppressWarnings("unchecked") public static final <T> T cast(Object obj, Type type, ParserConfig mapping) { if (obj == null) { return null; } if (type instanceof Class) { return (T) cast(obj, (Class<T>) type, mapping); } if (type instanceof ParameterizedType) { return (T) cast(obj, (ParameterizedType) type, mapping); } if (obj instanceof String) { String strVal = (String) obj; if (strVal.length() == 0) { return null; } } if (type instanceof TypeVariable) { return (T) obj; } throw new JSONException("can not cast to : " + type); } @SuppressWarnings({"rawtypes", "unchecked"}) public static final <T> T cast(Object obj, ParameterizedType type, ParserConfig mapping) { Type rawTye = type.getRawType(); if (rawTye == List.class || rawTye == ArrayList.class) { Type itemType = type.getActualTypeArguments()[0]; if (obj instanceof Iterable) { List list = new ArrayList(); for (Iterator it = ((Iterable) obj).iterator(); it.hasNext();) { Object item = it.next(); list.add(cast(item, itemType, mapping)); } return (T) list; } } if (rawTye == Map.class || rawTye == HashMap.class) { Type keyType = type.getActualTypeArguments()[0]; Type valueType = type.getActualTypeArguments()[1]; if (obj instanceof Map) { Map map = new HashMap(); for (Map.Entry entry : ((Map<?, ?>) obj).entrySet()) { Object key = cast(entry.getKey(), keyType, mapping); Object value = cast(entry.getValue(), valueType, mapping); map.put(key, value); } return (T) map; } } if (obj instanceof String) { String strVal = (String) obj; if (strVal.length() == 0) { return null; } } if (type.getActualTypeArguments().length == 1) { Type argType = type.getActualTypeArguments()[0]; if (argType instanceof WildcardType) { return (T) cast(obj, rawTye, mapping); } } throw new JSONException("can not cast to : " + type); } @SuppressWarnings({"unchecked"}) public static final <T> T castToJavaBean(Map<String, Object> map, Class<T> clazz, ParserConfig mapping) { try { if (clazz == StackTraceElement.class) { String declaringClass = (String) map.get("className"); String methodName = (String) map.get("methodName"); String fileName = (String) map.get("fileName"); int lineNumber; { Number value = (Number) map.get("lineNumber"); if (value == null) { lineNumber = 0; } else { lineNumber = value.intValue(); } } return (T) new StackTraceElement(declaringClass, methodName, fileName, lineNumber); } { Object iClassObject = map.get("@type"); if (iClassObject instanceof String) { String className = (String) iClassObject; clazz = (Class<T>) loadClass(className); if (clazz == null) { throw new ClassNotFoundException(className + " not found"); } } } if (clazz.isInterface()) { JSONObject object; if (map instanceof JSONObject) { object = (JSONObject) map; } else { object = new JSONObject(map); } return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{clazz}, object); } Map<String, FieldDeserializer> setters = mapping.getFieldDeserializers(clazz); T object = clazz.newInstance(); for (Map.Entry<String, FieldDeserializer> entry : setters.entrySet()) { String key = entry.getKey(); Method method = entry.getValue().getMethod(); if (isJSONTypeIgnore(clazz, key)) { continue; } if (map.containsKey(key)) { Object value = map.get(key); value = cast(value, method.getGenericParameterTypes()[0], mapping); method.invoke(object, new Object[]{value}); } } return object; } catch (Exception e) { throw new JSONException(e.getMessage(), e); } } private static ConcurrentMap<String, Class<?>> mappings = new ConcurrentHashMap<String, Class<?>>(); static { addBaseClassMappings(); } public static void addClassMapping(String className, Class<?> clazz) { if (className == null) { className = clazz.getName(); } mappings.put(className, clazz); } public static void addBaseClassMappings() { mappings.put("byte", byte.class); mappings.put("short", short.class); mappings.put("int", int.class); mappings.put("long", long.class); mappings.put("float", float.class); mappings.put("double", double.class); mappings.put("boolean", boolean.class); mappings.put("char", char.class); mappings.put("[byte", byte[].class); mappings.put("[short", short[].class); mappings.put("[int", int[].class); mappings.put("[long", long[].class); mappings.put("[float", float[].class); mappings.put("[double", double[].class); mappings.put("[boolean", boolean[].class); mappings.put("[char", char[].class); mappings.put(HashMap.class.getName(), HashMap.class); } public static void clearClassMapping() { mappings.clear(); addBaseClassMappings(); } public static Class<?> loadClass(String className) { if (className == null || className.length() == 0) { return null; } Class<?> clazz = mappings.get(className); if (clazz != null) { return clazz; } if (className.charAt(0) == '[') { Class<?> componentType = loadClass(className.substring(1)); return Array.newInstance(componentType, 0).getClass(); } if (className.startsWith("L") && className.endsWith(";")) { String newClassName = className.substring(1, className.length() - 1); return loadClass(newClassName); } try { clazz = Thread.currentThread().getContextClassLoader().loadClass(className); addClassMapping(className, clazz); return clazz; } catch (Throwable e) { // skip } try { clazz = Class.forName(className); addClassMapping(className, clazz); return clazz; } catch (Throwable e) { // skip } return clazz; } public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap) { return computeGetters(clazz, aliasMap, true); } public static List<FieldInfo> computeGetters(Class<?> clazz, Map<String, String> aliasMap, boolean sorted) { Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>(); for (Method method : clazz.getMethods()) { String methodName = method.getName(); if (Modifier.isStatic(method.getModifiers())) { continue; } if (method.getReturnType().equals(Void.TYPE)) { continue; } if (method.getParameterTypes().length != 0) { continue; } if (method.getReturnType() == ClassLoader.class) { continue; } if (method.getName().equals("getMetaClass") && method.getReturnType().getName().equals("groovy.lang.MetaClass")) { continue; } JSONField annotation = method.getAnnotation(JSONField.class); if (annotation != null) { if (!annotation.serialize()) { continue; } if (annotation.name().length() != 0) { String propertyName = annotation.name(); if (aliasMap != null) { propertyName = aliasMap.get(propertyName); if (propertyName == null) { continue; } } fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, null)); continue; } } if (methodName.startsWith("get")) { if (methodName.length() < 4) { continue; } if (methodName.equals("getClass")) { continue; } if (!Character.isUpperCase(methodName.charAt(3))) { continue; } String propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); if (isJSONTypeIgnore(clazz, propertyName)) { continue; } Field field = ParserConfig.getField(clazz, propertyName); if (field != null) { JSONField fieldAnnotation = field.getAnnotation(JSONField.class); if (fieldAnnotation != null && fieldAnnotation.name().length() != 0) { propertyName = fieldAnnotation.name(); if (aliasMap != null) { propertyName = aliasMap.get(propertyName); if (propertyName == null) { continue; } } } } if (aliasMap != null) { propertyName = aliasMap.get(propertyName); if (propertyName == null) { continue; } } fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field)); } if (methodName.startsWith("is")) { if (methodName.length() < 3) { continue; } if (!Character.isUpperCase(methodName.charAt(2))) { continue; } String propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3); if (isJSONTypeIgnore(clazz, propertyName)) { continue; } Field field = ParserConfig.getField(clazz, propertyName); if (field != null) { JSONField fieldAnnotation = field.getAnnotation(JSONField.class); if (fieldAnnotation != null && fieldAnnotation.name().length() != 0) { propertyName = fieldAnnotation.name(); if (aliasMap != null) { propertyName = aliasMap.get(propertyName); if (propertyName == null) { continue; } } } } if (aliasMap != null) { propertyName = aliasMap.get(propertyName); if (propertyName == null) { continue; } } fieldInfoMap.put(propertyName, new FieldInfo(propertyName, method, field)); } } for (Field field : clazz.getFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } if (!Modifier.isPublic(field.getModifiers())) { continue; } if (!fieldInfoMap.containsKey(field.getName())) { fieldInfoMap.put(field.getName(), new FieldInfo(field.getName(), null, field)); } } List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>(); boolean containsAll = false; String[] orders = null; JSONType annotation = clazz.getAnnotation(JSONType.class); if (annotation != null) { orders = annotation.orders(); if (orders != null && orders.length == fieldInfoMap.size()) { containsAll = true; for (String item : orders) { if (!fieldInfoMap.containsKey(item)) { containsAll = false; break; } } } else { containsAll = false; } } if (containsAll) { for (String item : orders) { FieldInfo fieldInfo = fieldInfoMap.get(item); fieldInfoList.add(fieldInfo); } } else { for (FieldInfo fieldInfo : fieldInfoMap.values()) { fieldInfoList.add(fieldInfo); } if (sorted) { Collections.sort(fieldInfoList); } } return fieldInfoList; } private static boolean isJSONTypeIgnore(Class<?> clazz, String propertyName) { JSONType jsonType = clazz.getAnnotation(JSONType.class); if (jsonType != null && jsonType.ignores() != null) { for (String item : jsonType.ignores()) { if (propertyName.equalsIgnoreCase(item)) { return true; } } } if (clazz.getSuperclass() != Object.class && clazz.getSuperclass() != null) { if (isJSONTypeIgnore(clazz.getSuperclass(), propertyName)) { return true; } } return false; } public static Class<?> getClass(Type type) { if (type.getClass() == Class.class) { return (Class<?>) type; } if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } return Object.class; } }