/*
* 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.serializer;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.sql.Clob;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONStreamAware;
import com.alibaba.fastjson.util.ServiceLoader;
/**
* @author wenshao<szujobs@hotmail.com>
*/
public class JSONSerializer {
private final SerializeConfig config;
private final SerializeWriter out;
private List<PropertyFilter> propertyFilters = null;
private List<ValueFilter> valueFilters = null;
private List<NameFilter> nameFilters = null;
private List<PropertyPreFilter> propertyPreFilters = null;
private int indentCount = 0;
private String indent = "\t";
private String dateFormatPatterm = JSON.DEFFAULT_DATE_FORMAT;
private DateFormat dateFormat;
private IdentityHashMap<Object, SerialContext> references = null;
private SerialContext context;
public JSONSerializer() {
this(new SerializeWriter(), SerializeConfig.getGlobalInstance());
}
public JSONSerializer(SerializeWriter out) {
this(out, SerializeConfig.getGlobalInstance());
}
public JSONSerializer(SerializeConfig config) {
this(new SerializeWriter(), config);
}
@Deprecated
public JSONSerializer(JSONSerializerMap mapping) {
this(new SerializeWriter(), mapping);
}
public JSONSerializer(SerializeWriter out, SerializeConfig config) {
this.out = out;
this.config = config;
}
public String getDateFormatPattern() {
return dateFormatPatterm;
}
public DateFormat getDateFormat() {
if (dateFormat == null) {
dateFormat = new SimpleDateFormat(dateFormatPatterm);
}
return dateFormat;
}
public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormatPatterm = dateFormat;
if (this.dateFormat != null) {
this.dateFormat = null;
}
}
public SerialContext getContext() {
return context;
}
public void setContext(SerialContext context) {
this.context = context;
}
public void setContext(SerialContext parent, Object object, Object fieldName) {
if (isEnabled(SerializerFeature.DisableCircularReferenceDetect)) {
return;
}
this.context = new SerialContext(parent, object, fieldName);
if (references == null) {
references = new IdentityHashMap<Object, SerialContext>();
}
this.references.put(object, context);
}
public void setContext(Object object, Object fieldName) {
this.setContext(context, object, fieldName);
}
public void popContext() {
if (context != null) {
this.context = this.context.getParent();
}
}
public void setContext(SerialContext parent, Object object) {
if (isEnabled(SerializerFeature.DisableCircularReferenceDetect)) {
return;
}
this.context = new SerialContext(parent, object, null);
if (references == null) {
references = new IdentityHashMap<Object, SerialContext>();
}
this.references.put(object, context);
}
public boolean isWriteClassName() {
return isEnabled(SerializerFeature.WriteClassName);
}
public final boolean isWriteClassName(Type fieldType, Object obj) {
boolean result = out.isEnabled(SerializerFeature.WriteClassName);
if (!result) {
return false;
}
if (fieldType == null) {
if (this.isEnabled(SerializerFeature.NotWriteRootClassName)) {
boolean isRoot = context.getParent() == null;
if (isRoot) {
return false;
}
}
}
return true;
}
public Collection<SerialContext> getReferences() {
if (references == null) {
references = new IdentityHashMap<Object, SerialContext>();
}
return references.values();
}
public SerialContext getSerialContext(Object object) {
if (references == null) {
return null;
}
return references.get(object);
}
public boolean containsReference(Object value) {
if (isEnabled(SerializerFeature.DisableCircularReferenceDetect)) {
return false;
}
if (references == null) {
return false;
}
return references.containsKey(value);
}
public void writeReference(Object object) {
if (isEnabled(SerializerFeature.DisableCircularReferenceDetect)) {
return;
}
SerialContext context = this.getContext();
Object current = context.getObject();
if (object == current) {
out.write("{\"$ref\":\"@\"}");
return;
}
SerialContext parentContext = context.getParent();
if (parentContext != null) {
if (object == parentContext.getObject()) {
out.write("{\"$ref\":\"..\"}");
return;
}
}
SerialContext rootContext = context;
for (;;) {
if (rootContext.getParent() == null) {
break;
}
rootContext = rootContext.getParent();
}
if (object == rootContext.getObject()) {
out.write("{\"$ref\":\"$\"}");
return;
}
SerialContext refContext = this.getSerialContext(object);
String path = refContext.getPath();
out.write("{\"$ref\":\"");
out.write(path);
out.write("\"}");
return;
}
public List<ValueFilter> getValueFilters() {
if (valueFilters == null) {
valueFilters = new ArrayList<ValueFilter>();
}
return valueFilters;
}
public List<ValueFilter> getValueFiltersDirect() {
return valueFilters;
}
public int getIndentCount() {
return indentCount;
}
public void incrementIndent() {
indentCount++;
}
public void decrementIdent() {
indentCount--;
}
public void println() {
out.write('\n');
for (int i = 0; i < indentCount; ++i) {
out.write(indent);
}
}
public List<NameFilter> getNameFilters() {
if (nameFilters == null) {
nameFilters = new ArrayList<NameFilter>();
}
return nameFilters;
}
public List<NameFilter> getNameFiltersDirect() {
return nameFilters;
}
public List<PropertyPreFilter> getPropertyPreFilters() {
if (propertyPreFilters == null) {
propertyPreFilters = new ArrayList<PropertyPreFilter>();
}
return propertyPreFilters;
}
public List<PropertyPreFilter> getPropertyPreFiltersDirect() {
return propertyPreFilters;
}
public List<PropertyFilter> getPropertyFilters() {
if (propertyFilters == null) {
propertyFilters = new ArrayList<PropertyFilter>();
}
return propertyFilters;
}
public List<PropertyFilter> getPropertyFiltersDirect() {
return propertyFilters;
}
public SerializeWriter getWriter() {
return out;
}
public String toString() {
return out.toString();
}
public void config(SerializerFeature feature, boolean state) {
out.config(feature, state);
}
public boolean isEnabled(SerializerFeature feature) {
return out.isEnabled(feature);
}
public void writeNull() {
this.out.writeNull();
}
public SerializeConfig getMapping() {
return config;
}
public static final void write(Writer out, Object object) {
SerializeWriter writer = new SerializeWriter();
try {
JSONSerializer serializer = new JSONSerializer(writer);
serializer.write(object);
writer.writeTo(out);
} catch (IOException ex) {
throw new JSONException(ex.getMessage(), ex);
} finally {
writer.close();
}
}
public static final void write(SerializeWriter out, Object object) {
JSONSerializer serializer = new JSONSerializer(out);
serializer.write(object);
}
public final void write(Object object) {
if (object == null) {
out.writeNull();
return;
}
Class<?> clazz = object.getClass();
ObjectSerializer writer = getObjectWriter(clazz);
try {
writer.write(this, object, null, null);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
}
public final void writeWithFieldName(Object object, Object fieldName) {
writeWithFieldName(object, fieldName, null);
}
public final void writeWithFieldName(Object object, Object fieldName, Type fieldType) {
try {
if (object == null) {
out.writeNull();
return;
}
Class<?> clazz = object.getClass();
ObjectSerializer writer = getObjectWriter(clazz);
writer.write(this, object, fieldName, fieldType);
} catch (IOException e) {
throw new JSONException(e.getMessage(), e);
}
}
public final void writeWithFormat(Object object, String format) {
if (object instanceof Date) {
String text = new SimpleDateFormat(format).format((Date) object);
out.writeString(text);
return;
}
write(object);
}
public final void write(String text) {
StringSerializer.instance.write(this, text);
}
public ObjectSerializer getObjectWriter(Class<?> clazz) {
ObjectSerializer writer = config.get(clazz);
if (writer == null) {
try {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
if (!(o instanceof AutowiredObjectSerializer)) {
continue;
}
AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
for (Type forType : autowired.getAutowiredFor()) {
config.put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
}
writer = config.get(clazz);
}
if (writer == null) {
final ClassLoader classLoader = JSON.class.getClassLoader();
if (classLoader != Thread.currentThread().getContextClassLoader()) {
try {
for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
if (!(o instanceof AutowiredObjectSerializer)) {
continue;
}
AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
for (Type forType : autowired.getAutowiredFor()) {
config.put(forType, autowired);
}
}
} catch (ClassCastException ex) {
// skip
}
writer = config.get(clazz);
}
}
if (writer == null) {
if (Map.class.isAssignableFrom(clazz)) {
config.put(clazz, MapSerializer.instance);
} else if (List.class.isAssignableFrom(clazz)) {
config.put(clazz, ListSerializer.instance);
} else if (Collection.class.isAssignableFrom(clazz)) {
config.put(clazz, CollectionSerializer.instance);
} else if (Date.class.isAssignableFrom(clazz)) {
config.put(clazz, DateSerializer.instance);
} else if (JSONAware.class.isAssignableFrom(clazz)) {
config.put(clazz, JSONAwareSerializer.instance);
} else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
config.put(clazz, JSONStreamAwareSerializer.instance);
} else if (clazz.isEnum() || (clazz.getSuperclass() != null && clazz.getSuperclass().isEnum())) {
config.put(clazz, EnumSerializer.instance);
} else if (clazz.isArray()) {
Class<?> componentType = clazz.getComponentType();
ObjectSerializer compObjectSerializer = getObjectWriter(componentType);
config.put(clazz, new ArraySerializer(componentType, compObjectSerializer));
} else if (Throwable.class.isAssignableFrom(clazz)) {
config.put(clazz, new ExceptionSerializer(clazz));
} else if (TimeZone.class.isAssignableFrom(clazz)) {
config.put(clazz, TimeZoneSerializer.instance);
} else if (Appendable.class.isAssignableFrom(clazz)) {
config.put(clazz, AppendableSerializer.instance);
} else if (Charset.class.isAssignableFrom(clazz)) {
config.put(clazz, CharsetSerializer.instance);
} else if (Enumeration.class.isAssignableFrom(clazz)) {
config.put(clazz, EnumerationSeriliazer.instance);
} else if (Calendar.class.isAssignableFrom(clazz)) {
config.put(clazz, CalendarSerializer.instance);
} else if (Clob.class.isAssignableFrom(clazz)) {
config.put(clazz, ClobSeriliazer.instance);
} else {
boolean isCglibProxy = false;
boolean isJavassistProxy = false;
for (Class<?> item : clazz.getInterfaces()) {
if (item.getName().equals("net.sf.cglib.proxy.Factory")) {
isCglibProxy = true;
break;
} else if (item.getName().equals("javassist.util.proxy.ProxyObject")) {
isJavassistProxy = true;
break;
}
}
if (isCglibProxy || isJavassistProxy) {
Class<?> superClazz = clazz.getSuperclass();
ObjectSerializer superWriter = getObjectWriter(superClazz);
config.put(clazz, superWriter);
return superWriter;
}
if (Proxy.isProxyClass(clazz)) {
config.put(clazz, config.createJavaBeanSerializer(clazz));
} else {
config.put(clazz, config.createJavaBeanSerializer(clazz));
}
}
writer = config.get(clazz);
}
return writer;
}
public void close() {
this.out.close();
}
}