/*
* 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.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.util.FieldInfo;
import com.alibaba.fastjson.util.TypeUtils;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class JavaBeanSerializer extends SerializeFilterable implements ObjectSerializer {
// serializers
protected final FieldSerializer[] getters;
protected final FieldSerializer[] sortedGetters;
protected SerializeBeanInfo beanInfo;
public JavaBeanSerializer(Class<?> beanType){
this(beanType, (Map<String, String>) null);
}
public JavaBeanSerializer(Class<?> beanType, String... aliasList){
this(beanType, createAliasMap(aliasList));
}
static Map<String, String> createAliasMap(String... aliasList) {
Map<String, String> aliasMap = new HashMap<String, String>();
for (String alias : aliasList) {
aliasMap.put(alias, alias);
}
return aliasMap;
}
public JavaBeanSerializer(Class<?> beanType, Map<String, String> aliasMap){
this(TypeUtils.buildBeanInfo(beanType, aliasMap, null));
}
public JavaBeanSerializer(SerializeBeanInfo beanInfo) {
this.beanInfo = beanInfo;
sortedGetters = new FieldSerializer[beanInfo.sortedFields.length];
for (int i = 0; i < sortedGetters.length; ++i) {
sortedGetters[i] = new FieldSerializer(beanInfo.beanType, beanInfo.sortedFields[i]);
}
if (beanInfo.fields == beanInfo.sortedFields) {
getters = sortedGetters;
} else {
getters = new FieldSerializer[beanInfo.fields.length];
for (int i = 0; i < getters.length; ++i) {
getters[i] = getFieldSerializer(beanInfo.fields[i].name);
}
}
}
public void writeDirectNonContext(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException {
write(serializer, object, fieldName, fieldType, features);
}
public void writeAsArray(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException {
write(serializer, object, fieldName, fieldType, features);
}
public void writeAsArrayNonContext(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException {
write(serializer, object, fieldName, fieldType, features);
}
public void write(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features) throws IOException {
write(serializer, object, fieldName, fieldType, features, false);
}
protected void write(JSONSerializer serializer, //
Object object, //
Object fieldName, //
Type fieldType, //
int features,
boolean unwrapped
) throws IOException {
SerializeWriter out = serializer.out;
if (object == null) {
out.writeNull();
return;
}
if (writeReference(serializer, object, features)) {
return;
}
final FieldSerializer[] getters;
if (out.sortField) {
getters = this.sortedGetters;
} else {
getters = this.getters;
}
SerialContext parent = serializer.context;
serializer.setContext(parent, object, fieldName, this.beanInfo.features, features);
final boolean writeAsArray = isWriteAsArray(serializer, features);
try {
final char startSeperator = writeAsArray ? '[' : '{';
final char endSeperator = writeAsArray ? ']' : '}';
if (!unwrapped) {
out.append(startSeperator);
}
if (getters.length > 0 && out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.incrementIndent();
serializer.println();
}
boolean commaFlag = false;
if ((this.beanInfo.features & SerializerFeature.WriteClassName.mask) != 0
|| serializer.isWriteClassName(fieldType, object)) {
Class<?> objClass = object.getClass();
if (objClass != fieldType) {
writeClassName(serializer, object);
commaFlag = true;
}
}
char seperator = commaFlag ? ',' : '\0';
final boolean directWritePrefix = out.quoteFieldNames && !out.useSingleQuotes;
char newSeperator = this.writeBefore(serializer, object, seperator);
commaFlag = newSeperator == ',';
final boolean skipTransient = out.isEnabled(SerializerFeature.SkipTransientField);
final boolean ignoreNonFieldGetter = out.isEnabled(SerializerFeature.IgnoreNonFieldGetter);
for (int i = 0; i < getters.length; ++i) {
FieldSerializer fieldSerializer = getters[i];
Field field = fieldSerializer.fieldInfo.field;
FieldInfo fieldInfo = fieldSerializer.fieldInfo;
String fieldInfoName = fieldInfo.name;
Class<?> fieldClass = fieldInfo.fieldClass;
if (skipTransient) {
if (field != null) {
if (fieldInfo.fieldTransient) {
continue;
}
}
}
if (ignoreNonFieldGetter) {
if (field == null) {
continue;
}
}
if ((!this.applyName(serializer, object, fieldInfo.name)) //
|| !this.applyLabel(serializer, fieldInfo.label)) {
continue;
}
Object propertyValue;
try {
propertyValue = fieldSerializer.getPropertyValueDirect(object);
} catch (InvocationTargetException ex) {
if (out.isEnabled(SerializerFeature.IgnoreErrorGetter)) {
propertyValue = null;
} else {
throw ex;
}
}
if (!this.apply(serializer, object, fieldInfoName, propertyValue)) {
continue;
}
String key = fieldInfoName;
key = this.processKey(serializer, object, key, propertyValue);
Object originalValue = propertyValue;
propertyValue = this.processValue(serializer, fieldSerializer.fieldContext, object, fieldInfoName,
propertyValue);
if (propertyValue == null && !writeAsArray) {
if ((!fieldSerializer.writeNull) && (!out.isEnabled(SerializerFeature.WRITE_MAP_NULL_FEATURES))) {
continue;
}
}
if (propertyValue != null //
&& (out.notWriteDefaultValue //
|| (fieldInfo.serialzeFeatures & SerializerFeature.NotWriteDefaultValue.mask) != 0 //
|| (beanInfo.features & SerializerFeature.NotWriteDefaultValue.mask) != 0 //
)) {
Class<?> fieldCLass = fieldInfo.fieldClass;
if (fieldCLass == byte.class && propertyValue instanceof Byte
&& ((Byte) propertyValue).byteValue() == 0) {
continue;
} else if (fieldCLass == short.class && propertyValue instanceof Short
&& ((Short) propertyValue).shortValue() == 0) {
continue;
} else if (fieldCLass == int.class && propertyValue instanceof Integer
&& ((Integer) propertyValue).intValue() == 0) {
continue;
} else if (fieldCLass == long.class && propertyValue instanceof Long
&& ((Long) propertyValue).longValue() == 0L) {
continue;
} else if (fieldCLass == float.class && propertyValue instanceof Float
&& ((Float) propertyValue).floatValue() == 0F) {
continue;
} else if (fieldCLass == double.class && propertyValue instanceof Double
&& ((Double) propertyValue).doubleValue() == 0D) {
continue;
} else if (fieldCLass == boolean.class && propertyValue instanceof Boolean
&& !((Boolean) propertyValue).booleanValue()) {
continue;
}
}
if (commaFlag) {
if (fieldInfo.unwrapped
&& propertyValue instanceof Map
&& ((Map) propertyValue).size() == 0) {
continue;
}
out.write(',');
if (out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.println();
}
}
if (key != fieldInfoName) {
if (!writeAsArray) {
out.writeFieldName(key, true);
}
serializer.write(propertyValue);
} else if (originalValue != propertyValue) {
if (!writeAsArray) {
fieldSerializer.writePrefix(serializer);
}
serializer.write(propertyValue);
} else {
if (!writeAsArray) {
if (!fieldInfo.unwrapped) {
if (directWritePrefix) {
out.write(fieldInfo.name_chars, 0, fieldInfo.name_chars.length);
} else {
fieldSerializer.writePrefix(serializer);
}
}
}
if (!writeAsArray) {
JSONField fieldAnnotation = fieldInfo.getAnnotation();
if (fieldClass == String.class && (fieldAnnotation == null || fieldAnnotation.serializeUsing() == Void.class)) {
if (propertyValue == null) {
if ((out.features & SerializerFeature.WriteNullStringAsEmpty.mask) != 0
|| (fieldSerializer.features
& SerializerFeature.WriteNullStringAsEmpty.mask) != 0) {
out.writeString("");
} else {
out.writeNull();
}
} else {
String propertyValueString = (String) propertyValue;
if (out.useSingleQuotes) {
out.writeStringWithSingleQuote(propertyValueString);
} else {
out.writeStringWithDoubleQuote(propertyValueString, (char) 0);
}
}
} else {
if (fieldInfo.unwrapped
&& propertyValue instanceof Map
&& ((Map) propertyValue).size() == 0) {
commaFlag = false;
continue;
}
fieldSerializer.writeValue(serializer, propertyValue);
}
} else {
fieldSerializer.writeValue(serializer, propertyValue);
}
}
commaFlag = true;
}
this.writeAfter(serializer, object, commaFlag ? ',' : '\0');
if (getters.length > 0 && out.isEnabled(SerializerFeature.PrettyFormat)) {
serializer.decrementIdent();
serializer.println();
}
if (!unwrapped) {
out.append(endSeperator);
}
} catch (Exception e) {
String errorMessage = "write javaBean error";
if (object != null) {
errorMessage += ", class " + object.getClass().getName();
}
if (fieldName != null) {
errorMessage += ", fieldName : " + fieldName;
}
if (e.getMessage() != null) {
errorMessage += (", " + e.getMessage());
}
throw new JSONException(errorMessage, e);
} finally {
serializer.context = parent;
}
}
protected void writeClassName(JSONSerializer serializer, Object object) {
serializer.out.writeFieldName(serializer.config.typeKey, false);
String typeName = this.beanInfo.typeName;
if (typeName == null) {
Class<?> clazz = object.getClass();
if (TypeUtils.isProxy(clazz)) {
clazz = clazz.getSuperclass();
}
typeName = clazz.getName();
}
serializer.write(typeName);
}
public boolean writeReference(JSONSerializer serializer, Object object, int fieldFeatures) {
SerialContext context = serializer.context;
int mask = SerializerFeature.DisableCircularReferenceDetect.mask;
if (context == null || (context.features & mask) != 0 || (fieldFeatures & mask) != 0) {
return false;
}
if (serializer.references != null && serializer.references.containsKey(object)) {
serializer.writeReference(object);
return true;
} else {
return false;
}
}
protected boolean isWriteAsArray(JSONSerializer serializer) {
return isWriteAsArray(serializer, 0);
}
protected boolean isWriteAsArray(JSONSerializer serializer, int fieldFeatrues) {
final int mask = SerializerFeature.BeanToArray.mask;
return (beanInfo.features & mask) != 0 //
|| serializer.out.beanToArray //
|| (fieldFeatrues & mask) != 0;
}
public Object getFieldValue(Object object, String key) {
FieldSerializer fieldDeser = getFieldSerializer(key);
if (fieldDeser == null) {
throw new JSONException("field not found. " + key);
}
try {
return fieldDeser.getPropertyValue(object);
} catch (InvocationTargetException ex) {
throw new JSONException("getFieldValue error." + key, ex);
} catch (IllegalAccessException ex) {
throw new JSONException("getFieldValue error." + key, ex);
}
}
public FieldSerializer getFieldSerializer(String key) {
if (key == null) {
return null;
}
int low = 0;
int high = sortedGetters.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
String fieldName = sortedGetters[mid].fieldInfo.name;
int cmp = fieldName.compareTo(key);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return sortedGetters[mid]; // key found
}
}
return null; // key not found.
}
public List<Object> getFieldValues(Object object) throws Exception {
List<Object> fieldValues = new ArrayList<Object>(sortedGetters.length);
for (FieldSerializer getter : sortedGetters) {
fieldValues.add(getter.getPropertyValue(object));
}
return fieldValues;
}
// for jsonpath deepSet
public List<Object> getObjectFieldValues(Object object) throws Exception {
List<Object> fieldValues = new ArrayList<Object>(sortedGetters.length);
for (FieldSerializer getter : sortedGetters) {
Class fieldClass = getter.fieldInfo.fieldClass;
if (fieldClass.isPrimitive()) {
continue;
}
if (fieldClass.getName().startsWith("java.lang.")) {
continue;
}
fieldValues.add(getter.getPropertyValue(object));
}
return fieldValues;
}
public int getSize(Object object) throws Exception {
int size = 0;
for (FieldSerializer getter : sortedGetters) {
Object value = getter.getPropertyValueDirect(object);
if (value != null) {
size ++;
}
}
return size;
}
public Map<String, Object> getFieldValuesMap(Object object) throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>(sortedGetters.length);
for (FieldSerializer getter : sortedGetters) {
map.put(getter.fieldInfo.name, getter.getPropertyValue(object));
}
return map;
}
protected BeanContext getBeanContext(int orinal) {
return sortedGetters[orinal].fieldContext;
}
protected Type getFieldType(int ordinal) {
return sortedGetters[ordinal].fieldInfo.fieldType;
}
protected char writeBefore(JSONSerializer jsonBeanDeser, //
Object object, char seperator) {
if (jsonBeanDeser.beforeFilters != null) {
for (BeforeFilter beforeFilter : jsonBeanDeser.beforeFilters) {
seperator = beforeFilter.writeBefore(jsonBeanDeser, object, seperator);
}
}
if (this.beforeFilters != null) {
for (BeforeFilter beforeFilter : this.beforeFilters) {
seperator = beforeFilter.writeBefore(jsonBeanDeser, object, seperator);
}
}
return seperator;
}
protected char writeAfter(JSONSerializer jsonBeanDeser, //
Object object, char seperator) {
if (jsonBeanDeser.afterFilters != null) {
for (AfterFilter afterFilter : jsonBeanDeser.afterFilters) {
seperator = afterFilter.writeAfter(jsonBeanDeser, object, seperator);
}
}
if (this.afterFilters != null) {
for (AfterFilter afterFilter : this.afterFilters) {
seperator = afterFilter.writeAfter(jsonBeanDeser, object, seperator);
}
}
return seperator;
}
protected boolean applyLabel(JSONSerializer jsonBeanDeser, String label) {
if (jsonBeanDeser.labelFilters != null) {
for (LabelFilter propertyFilter : jsonBeanDeser.labelFilters) {
if (!propertyFilter.apply(label)) {
return false;
}
}
}
if (this.labelFilters != null) {
for (LabelFilter propertyFilter : this.labelFilters) {
if (!propertyFilter.apply(label)) {
return false;
}
}
}
return true;
}
}