/* * Copyright 1999-2017 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.serializer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.util.FieldInfo; import com.alibaba.fastjson.util.TypeUtils; /** * @author wenshao[szujobs@hotmail.com] */ public class FieldSerializer implements Comparable<FieldSerializer> { public final FieldInfo fieldInfo; protected final boolean writeNull; protected int features; private final String double_quoted_fieldPrefix; private String single_quoted_fieldPrefix; private String un_quoted_fieldPrefix; protected BeanContext fieldContext; private String format; protected boolean writeEnumUsingToString = false; protected boolean writeEnumUsingName = false; protected boolean serializeUsing = false; protected boolean persistenceOneToMany = false; private RuntimeSerializerInfo runtimeInfo; public FieldSerializer(Class<?> beanType, FieldInfo fieldInfo) { this.fieldInfo = fieldInfo; this.fieldContext = new BeanContext(beanType, fieldInfo); if (beanType != null && fieldInfo.isEnum) { JSONType jsonType = beanType.getAnnotation(JSONType.class); if (jsonType != null) { for (SerializerFeature feature : jsonType.serialzeFeatures()) { if (feature == SerializerFeature.WriteEnumUsingToString) { writeEnumUsingToString = true; }else if(feature == SerializerFeature.WriteEnumUsingName){ writeEnumUsingName = true; } } } } fieldInfo.setAccessible(); this.double_quoted_fieldPrefix = '"' + fieldInfo.name + "\":"; boolean writeNull = false; JSONField annotation = fieldInfo.getAnnotation(); if (annotation != null) { for (SerializerFeature feature : annotation.serialzeFeatures()) { if ((feature.getMask() & SerializerFeature.WRITE_MAP_NULL_FEATURES) != 0) { writeNull = true; break; } } format = annotation.format(); if (format.trim().length() == 0) { format = null; } for (SerializerFeature feature : annotation.serialzeFeatures()) { if (feature == SerializerFeature.WriteEnumUsingToString) { writeEnumUsingToString = true; }else if(feature == SerializerFeature.WriteEnumUsingName){ writeEnumUsingName = true; } } features = SerializerFeature.of(annotation.serialzeFeatures()); } this.writeNull = writeNull; persistenceOneToMany = TypeUtils.isAnnotationPresentOneToMany(fieldInfo.method); } public void writePrefix(JSONSerializer serializer) throws IOException { SerializeWriter out = serializer.out; if (out.quoteFieldNames) { if (out.useSingleQuotes) { if (single_quoted_fieldPrefix == null) { single_quoted_fieldPrefix = '\'' + fieldInfo.name + "\':"; } out.write(single_quoted_fieldPrefix); } else { out.write(double_quoted_fieldPrefix); } } else { if (un_quoted_fieldPrefix == null) { this.un_quoted_fieldPrefix = fieldInfo.name + ":"; } out.write(un_quoted_fieldPrefix); } } public Object getPropertyValueDirect(Object object) throws InvocationTargetException, IllegalAccessException { Object fieldValue = fieldInfo.get(object); if (persistenceOneToMany && TypeUtils.isHibernateInitialized(fieldValue)) { return null; } return fieldValue; } public Object getPropertyValue(Object object) throws InvocationTargetException, IllegalAccessException { Object propertyValue = fieldInfo.get(object); if (format != null && propertyValue != null) { if (fieldInfo.fieldClass == Date.class) { SimpleDateFormat dateFormat = new SimpleDateFormat(format); dateFormat.setTimeZone(JSON.defaultTimeZone); return dateFormat.format(propertyValue); } } return propertyValue; } public int compareTo(FieldSerializer o) { return this.fieldInfo.compareTo(o.fieldInfo); } public void writeValue(JSONSerializer serializer, Object propertyValue) throws Exception { if (runtimeInfo == null) { Class<?> runtimeFieldClass; if (propertyValue == null) { runtimeFieldClass = this.fieldInfo.fieldClass; } else { runtimeFieldClass = propertyValue.getClass(); } ObjectSerializer fieldSerializer = null; JSONField fieldAnnotation = fieldInfo.getAnnotation(); if (fieldAnnotation != null && fieldAnnotation.serializeUsing() != Void.class) { fieldSerializer = (ObjectSerializer) fieldAnnotation.serializeUsing().newInstance(); serializeUsing = true; } else { if (format != null) { if (runtimeFieldClass == double.class || runtimeFieldClass == Double.class) { fieldSerializer = new DoubleSerializer(format); } else if (runtimeFieldClass == float.class || runtimeFieldClass == Float.class) { fieldSerializer = new FloatCodec(format); } } if (fieldSerializer == null) { fieldSerializer = serializer.getObjectWriter(runtimeFieldClass); } } runtimeInfo = new RuntimeSerializerInfo(fieldSerializer, runtimeFieldClass); } final RuntimeSerializerInfo runtimeInfo = this.runtimeInfo; final int fieldFeatures = fieldInfo.serialzeFeatures; if (propertyValue == null) { Class<?> runtimeFieldClass = runtimeInfo.runtimeFieldClass; SerializeWriter out = serializer.out; if (Number.class.isAssignableFrom(runtimeFieldClass)) { out.writeNull(features, SerializerFeature.WriteNullNumberAsZero.mask); return; } else if (String.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullStringAsEmpty.mask); return; } else if (Boolean.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullBooleanAsFalse.mask); return; } else if (Collection.class.isAssignableFrom(runtimeFieldClass)) { out.writeNull(features, SerializerFeature.WriteNullListAsEmpty.mask); return; } ObjectSerializer fieldSerializer = runtimeInfo.fieldSerializer; if ((out.isEnabled(SerializerFeature.WRITE_MAP_NULL_FEATURES)) && fieldSerializer instanceof JavaBeanSerializer) { out.writeNull(); return; } fieldSerializer.write(serializer, null, fieldInfo.name, fieldInfo.fieldType, fieldFeatures); return; } if (fieldInfo.isEnum) { if (writeEnumUsingName) { serializer.out.writeString(((Enum<?>) propertyValue).name()); return; } if (writeEnumUsingToString) { serializer.out.writeString(((Enum<?>) propertyValue).toString()); return; } } Class<?> valueClass = propertyValue.getClass(); ObjectSerializer valueSerializer; if (valueClass == runtimeInfo.runtimeFieldClass || serializeUsing) { valueSerializer = runtimeInfo.fieldSerializer; } else { valueSerializer = serializer.getObjectWriter(valueClass); } if (format != null && !(valueSerializer instanceof DoubleSerializer || valueSerializer instanceof FloatCodec)) { if (valueSerializer instanceof ContextObjectSerializer) { ((ContextObjectSerializer) valueSerializer).write(serializer, propertyValue, this.fieldContext); } else { serializer.writeWithFormat(propertyValue, format); } return; } if (fieldInfo.unwrapped) { if (valueSerializer instanceof JavaBeanSerializer) { JavaBeanSerializer javaBeanSerializer = (JavaBeanSerializer) valueSerializer; javaBeanSerializer.write(serializer, propertyValue, fieldInfo.name, fieldInfo.fieldType, fieldFeatures, true); return; } if (valueSerializer instanceof MapSerializer) { MapSerializer mapSerializer = (MapSerializer) valueSerializer; mapSerializer.write(serializer, propertyValue, fieldInfo.name, fieldInfo.fieldType, fieldFeatures, true); return; } } valueSerializer.write(serializer, propertyValue, fieldInfo.name, fieldInfo.fieldType, fieldFeatures); } static class RuntimeSerializerInfo { final ObjectSerializer fieldSerializer; final Class<?> runtimeFieldClass; public RuntimeSerializerInfo(ObjectSerializer fieldSerializer, Class<?> runtimeFieldClass){ this.fieldSerializer = fieldSerializer; this.runtimeFieldClass = runtimeFieldClass; } } }