com.alibaba.fastjson2.writer.ObjectWriterAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fastjson2 Show documentation
Show all versions of fastjson2 Show documentation
Fastjson is a JSON processor (JSON parser + JSON generator) written in Java
package com.alibaba.fastjson2.writer;
import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.codec.FieldInfo;
import com.alibaba.fastjson2.filter.*;
import com.alibaba.fastjson2.util.BeanUtils;
import com.alibaba.fastjson2.util.DateUtils;
import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.TypeUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import static com.alibaba.fastjson2.JSONB.Constants.BC_TYPED_ANY;
import static com.alibaba.fastjson2.JSONWriter.Feature.*;
public class ObjectWriterAdapter
implements ObjectWriter {
boolean hasFilter;
PropertyPreFilter propertyPreFilter;
PropertyFilter propertyFilter;
NameFilter nameFilter;
ValueFilter valueFilter;
static final String TYPE = "@type";
final Class objectClass;
final List fieldWriters;
protected final FieldWriter[] fieldWriterArray;
final String typeKey;
byte[] typeKeyJSONB;
protected final String typeName;
protected final long typeNameHash;
protected long typeNameSymbolCache;
protected final byte[] typeNameJSONB;
byte[] nameWithColonUTF8;
char[] nameWithColonUTF16;
final long features;
final long[] hashCodes;
final short[] mapping;
final boolean hasValueField;
final boolean serializable;
final boolean containsNoneFieldGetter;
final boolean googleCollection;
public ObjectWriterAdapter(Class objectClass, List fieldWriters) {
this(objectClass, null, null, 0, fieldWriters);
}
public ObjectWriterAdapter(
Class objectClass,
String typeKey,
String typeName,
long features,
List fieldWriters
) {
if (typeName == null && objectClass != null) {
if (Enum.class.isAssignableFrom(objectClass) && !objectClass.isEnum()) {
typeName = objectClass.getSuperclass().getName();
} else {
typeName = TypeUtils.getTypeName(objectClass);
}
}
this.objectClass = objectClass;
this.typeKey = typeKey == null || typeKey.isEmpty() ? TYPE : typeKey;
this.typeName = typeName;
this.typeNameHash = typeName != null ? Fnv.hashCode64(typeName) : 0;
this.typeNameJSONB = JSONB.toBytes(typeName);
this.features = features;
this.fieldWriters = fieldWriters;
this.serializable = objectClass == null || java.io.Serializable.class.isAssignableFrom(objectClass);
this.googleCollection =
"com.google.common.collect.AbstractMapBasedMultimap$RandomAccessWrappedList".equals(typeName)
|| "com.google.common.collect.AbstractMapBasedMultimap$WrappedSet".equals(typeName);
this.fieldWriterArray = new FieldWriter[fieldWriters.size()];
fieldWriters.toArray(fieldWriterArray);
this.hasValueField = fieldWriterArray.length == 1 && (fieldWriterArray[0].features & FieldInfo.VALUE_MASK) != 0;
boolean containsNoneFieldGetter = false;
long[] hashCodes = new long[fieldWriterArray.length];
for (int i = 0; i < fieldWriterArray.length; i++) {
FieldWriter fieldWriter = fieldWriterArray[i];
long hashCode = Fnv.hashCode64(fieldWriter.fieldName);
hashCodes[i] = hashCode;
if (fieldWriter.method != null && (fieldWriter.features & FieldInfo.FIELD_MASK) == 0) {
containsNoneFieldGetter = true;
}
}
this.containsNoneFieldGetter = containsNoneFieldGetter;
this.hashCodes = Arrays.copyOf(hashCodes, hashCodes.length);
Arrays.sort(this.hashCodes);
mapping = new short[this.hashCodes.length];
for (int i = 0; i < hashCodes.length; i++) {
long hashCode = hashCodes[i];
int index = Arrays.binarySearch(this.hashCodes, hashCode);
mapping[index] = (short) i;
}
}
@Override
public long getFeatures() {
return features;
}
@Override
public FieldWriter getFieldWriter(long hashCode) {
int m = Arrays.binarySearch(hashCodes, hashCode);
if (m < 0) {
return null;
}
int index = this.mapping[m];
return fieldWriterArray[index];
}
@Override
public final boolean hasFilter(JSONWriter jsonWriter) {
return hasFilter || jsonWriter.hasFilter(containsNoneFieldGetter);
}
public void setPropertyFilter(PropertyFilter propertyFilter) {
this.propertyFilter = propertyFilter;
if (propertyFilter != null) {
hasFilter = true;
}
}
public void setValueFilter(ValueFilter valueFilter) {
this.valueFilter = valueFilter;
if (valueFilter != null) {
hasFilter = true;
}
}
public void setNameFilter(NameFilter nameFilter) {
this.nameFilter = nameFilter;
if (nameFilter != null) {
hasFilter = true;
}
}
public void setPropertyPreFilter(PropertyPreFilter propertyPreFilter) {
this.propertyPreFilter = propertyPreFilter;
if (propertyPreFilter != null) {
hasFilter = true;
}
}
@Override
public void writeArrayMappingJSONB(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
if (jsonWriter.isWriteTypeInfo(object, fieldType, features)) {
writeClassInfo(jsonWriter);
}
int size = fieldWriters.size();
jsonWriter.startArray(size);
for (int i = 0; i < size; ++i) {
FieldWriter fieldWriter = fieldWriters.get(i);
fieldWriter.writeValue(jsonWriter, object);
}
}
@Override
public void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
long featuresAll = features | this.features | jsonWriter.getFeatures();
if (!serializable) {
if ((featuresAll & JSONWriter.Feature.ErrorOnNoneSerializable.mask) != 0) {
errorOnNoneSerializable();
return;
}
if ((featuresAll & JSONWriter.Feature.IgnoreNoneSerializable.mask) != 0) {
jsonWriter.writeNull();
return;
}
}
if ((featuresAll & JSONWriter.Feature.IgnoreNoneSerializable.mask) != 0) {
writeWithFilter(jsonWriter, object, fieldName, fieldType, features);
return;
}
int size = fieldWriterArray.length;
if (jsonWriter.isWriteTypeInfo(object, fieldType, features)) {
writeClassInfo(jsonWriter);
}
jsonWriter.startObject();
for (int i = 0; i < size; ++i) {
fieldWriters.get(i)
.write(jsonWriter, object);
}
jsonWriter.endObject();
}
protected final void writeClassInfo(JSONWriter jsonWriter) {
SymbolTable symbolTable = jsonWriter.symbolTable;
if (symbolTable != null) {
if (writeClassInfoSymbol(jsonWriter, symbolTable)) {
return;
}
}
jsonWriter.writeTypeName(typeNameJSONB, typeNameHash);
}
private boolean writeClassInfoSymbol(JSONWriter jsonWriter, SymbolTable symbolTable) {
int symbolTableIdentity = System.identityHashCode(symbolTable);
int symbol;
if (typeNameSymbolCache == 0) {
symbol = symbolTable.getOrdinalByHashCode(typeNameHash);
if (symbol != -1) {
typeNameSymbolCache = ((long) symbol << 32) | symbolTableIdentity;
}
} else {
int identity = (int) typeNameSymbolCache;
if (identity == symbolTableIdentity) {
symbol = (int) (typeNameSymbolCache >> 32);
} else {
symbol = symbolTable.getOrdinalByHashCode(typeNameHash);
if (symbol != -1) {
typeNameSymbolCache = ((long) symbol << 32) | symbolTableIdentity;
}
}
}
if (symbol != -1) {
jsonWriter.writeRaw(BC_TYPED_ANY);
jsonWriter.writeInt32(-symbol);
return true;
}
return false;
}
@Override
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
if (hasValueField) {
FieldWriter fieldWriter = fieldWriterArray[0];
fieldWriter.writeValue(jsonWriter, object);
return;
}
long featuresAll = features | this.features | jsonWriter.getFeatures();
boolean beanToArray = (featuresAll & BeanToArray.mask) != 0;
if (jsonWriter.jsonb) {
if (beanToArray) {
writeArrayMappingJSONB(jsonWriter, object, fieldName, fieldType, features);
return;
}
writeJSONB(jsonWriter, object, fieldName, fieldType, features);
return;
}
if (googleCollection) {
Collection collection = (Collection) object;
ObjectWriterImplCollection.INSTANCE.write(jsonWriter, collection, fieldName, fieldType, features);
return;
}
if (beanToArray) {
writeArrayMapping(jsonWriter, object, fieldName, fieldType, features);
return;
}
if (!serializable) {
if ((featuresAll & JSONWriter.Feature.ErrorOnNoneSerializable.mask) != 0) {
errorOnNoneSerializable();
return;
}
if ((featuresAll & JSONWriter.Feature.IgnoreNoneSerializable.mask) != 0) {
jsonWriter.writeNull();
return;
}
}
if (hasFilter(jsonWriter)) {
writeWithFilter(jsonWriter, object, fieldName, fieldType, features);
return;
}
jsonWriter.startObject();
if (((features | this.features) & WriteClassName.mask) != 0 || jsonWriter.isWriteTypeInfo(object, features)) {
writeTypeInfo(jsonWriter);
}
final int size = fieldWriters.size();
for (int i = 0; i < size; i++) {
FieldWriter fieldWriter = fieldWriters.get(i);
fieldWriter.write(jsonWriter, object);
}
jsonWriter.endObject();
}
public Map toMap(Object object) {
final int size = fieldWriters.size();
JSONObject map = new JSONObject(size, 1F);
for (int i = 0; i < size; i++) {
FieldWriter fieldWriter = fieldWriters.get(i);
map.put(
fieldWriter.fieldName,
fieldWriter.getFieldValue(object)
);
}
return map;
}
@Override
public List getFieldWriters() {
return fieldWriters;
}
byte[] jsonbClassInfo;
@Override
public boolean writeTypeInfo(JSONWriter jsonWriter) {
if (jsonWriter.utf8) {
if (nameWithColonUTF8 == null) {
int typeKeyLength = typeKey.length();
int typeNameLength = typeName.length();
byte[] chars = new byte[typeKeyLength + typeNameLength + 5];
chars[0] = '"';
typeKey.getBytes(0, typeKeyLength, chars, 1);
chars[typeKeyLength + 1] = '"';
chars[typeKeyLength + 2] = ':';
chars[typeKeyLength + 3] = '"';
typeName.getBytes(0, typeNameLength, chars, typeKeyLength + 4);
chars[typeKeyLength + typeNameLength + 4] = '"';
nameWithColonUTF8 = chars;
}
jsonWriter.writeNameRaw(nameWithColonUTF8);
return true;
} else if (jsonWriter.utf16) {
if (nameWithColonUTF16 == null) {
int typeKeyLength = typeKey.length();
int typeNameLength = typeName.length();
char[] chars = new char[typeKeyLength + typeNameLength + 5];
chars[0] = '"';
typeKey.getChars(0, typeKeyLength, chars, 1);
chars[typeKeyLength + 1] = '"';
chars[typeKeyLength + 2] = ':';
chars[typeKeyLength + 3] = '"';
typeName.getChars(0, typeNameLength, chars, typeKeyLength + 4);
chars[typeKeyLength + typeNameLength + 4] = '"';
nameWithColonUTF16 = chars;
}
jsonWriter.writeNameRaw(nameWithColonUTF16);
return true;
} else if (jsonWriter.jsonb) {
if (typeKeyJSONB == null) {
typeKeyJSONB = JSONB.toBytes(typeKey);
}
jsonWriter.writeRaw(typeKeyJSONB);
jsonWriter.writeRaw(typeNameJSONB);
return true;
}
jsonWriter.writeString(typeKey);
jsonWriter.writeColon();
jsonWriter.writeString(typeName);
return true;
}
@Override
public void writeWithFilter(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
if (object == null) {
jsonWriter.writeNull();
return;
}
if (jsonWriter.isWriteTypeInfo(object, fieldType, features)) {
if (jsonWriter.jsonb) {
writeClassInfo(jsonWriter);
jsonWriter.startObject();
} else {
jsonWriter.startObject();
writeTypeInfo(jsonWriter);
}
} else {
jsonWriter.startObject();
}
JSONWriter.Context context = jsonWriter.context;
long features2 = context.getFeatures() | features;
boolean refDetect = (features2 & ReferenceDetection.mask) != 0;
boolean ignoreNonFieldGetter = (features2 & IgnoreNonFieldGetter.mask) != 0;
BeforeFilter beforeFilter = context.getBeforeFilter();
if (beforeFilter != null) {
beforeFilter.writeBefore(jsonWriter, object);
}
PropertyPreFilter propertyPreFilter = context.getPropertyPreFilter();
if (propertyPreFilter == null) {
propertyPreFilter = this.propertyPreFilter;
}
NameFilter nameFilter = context.getNameFilter();
if (nameFilter == null) {
nameFilter = this.nameFilter;
} else {
if (this.nameFilter != null) {
nameFilter = NameFilter.compose(this.nameFilter, nameFilter);
}
}
ContextNameFilter contextNameFilter = context.getContextNameFilter();
ValueFilter valueFilter = context.getValueFilter();
if (valueFilter == null) {
valueFilter = this.valueFilter;
} else {
if (this.valueFilter != null) {
valueFilter = ValueFilter.compose(this.valueFilter, valueFilter);
}
}
ContextValueFilter contextValueFilter = context.getContextValueFilter();
PropertyFilter propertyFilter = context.getPropertyFilter();
if (propertyFilter == null) {
propertyFilter = this.propertyFilter;
}
LabelFilter labelFilter = context.getLabelFilter();
for (int i = 0; i < fieldWriters.size(); i++) {
FieldWriter fieldWriter = fieldWriters.get(i);
Field field = fieldWriter.field;
if (ignoreNonFieldGetter
&& fieldWriter.method != null
&& (fieldWriter.features & FieldInfo.FIELD_MASK) == 0) {
continue;
}
// pre property filter
final String fieldWriterFieldName = fieldWriter.fieldName;
if (propertyPreFilter != null
&& !propertyPreFilter.process(jsonWriter, object, fieldWriterFieldName)) {
continue;
}
if (labelFilter != null) {
String label = fieldWriter.label;
if (label != null && !label.isEmpty()) {
if (!labelFilter.apply(label)) {
continue;
}
}
}
// fast return
if (nameFilter == null
&& propertyFilter == null
&& contextValueFilter == null
&& contextNameFilter == null
&& valueFilter == null
) {
fieldWriter.write(jsonWriter, object);
continue;
}
Object fieldValue;
try {
fieldValue = fieldWriter.getFieldValue(object);
} catch (Throwable e) {
if ((context.getFeatures() & JSONWriter.Feature.IgnoreErrorGetter.mask) != 0) {
continue;
}
throw e;
}
if (fieldValue == null && !jsonWriter.isWriteNulls()) {
continue;
}
if (!refDetect && ("this$0".equals(fieldWriterFieldName) || "this$1".equals(fieldWriterFieldName) || "this$2".equals(fieldWriterFieldName))) {
continue;
}
BeanContext beanContext = null;
// name filter
String filteredName = fieldWriterFieldName;
if (nameFilter != null) {
filteredName = nameFilter.process(object, filteredName, fieldValue);
}
if (contextNameFilter != null) {
if (beanContext == null) {
if (field == null && fieldWriter.method != null) {
field = BeanUtils.getDeclaredField(objectClass, fieldWriter.fieldName);
}
beanContext = new BeanContext(
objectClass,
fieldWriter.method,
field,
fieldWriter.fieldName,
fieldWriter.label,
fieldWriter.fieldClass,
fieldWriter.fieldType,
fieldWriter.features,
fieldWriter.format
);
filteredName = contextNameFilter.process(beanContext, object, filteredName, fieldValue);
}
}
// property filter
if (propertyFilter != null
&& !propertyFilter.apply(object, fieldWriterFieldName, fieldValue)) {
continue;
}
boolean nameChanged = filteredName != null && filteredName != fieldWriterFieldName;
Object filteredValue = fieldValue;
if (valueFilter != null) {
filteredValue = valueFilter.apply(object, fieldWriterFieldName, fieldValue);
}
if (contextValueFilter != null) {
if (beanContext == null) {
if (field == null && fieldWriter.method != null) {
field = BeanUtils.getDeclaredField(objectClass, fieldWriter.fieldName);
}
beanContext = new BeanContext(
objectClass,
fieldWriter.method,
field,
fieldWriter.fieldName,
fieldWriter.label,
fieldWriter.fieldClass,
fieldWriter.fieldType,
fieldWriter.features,
fieldWriter.format
);
}
filteredValue = contextValueFilter.process(beanContext, object, filteredName, filteredValue);
}
if (filteredValue != fieldValue) {
if (nameChanged) {
jsonWriter.writeName(filteredName);
jsonWriter.writeColon();
} else {
fieldWriter.writeFieldName(jsonWriter);
}
if (filteredValue == null) {
jsonWriter.writeNull();
} else {
ObjectWriter fieldValueWriter = fieldWriter.getObjectWriter(jsonWriter, filteredValue.getClass());
fieldValueWriter.write(jsonWriter, filteredValue, fieldName, fieldType, features);
}
} else {
if (!nameChanged) {
fieldWriter.write(jsonWriter, object);
} else {
jsonWriter.writeName(filteredName);
jsonWriter.writeColon();
if (fieldValue == null) {
ObjectWriter fieldValueWriter = fieldWriter.getObjectWriter(jsonWriter, fieldWriter.fieldClass);
fieldValueWriter.write(jsonWriter, null, fieldName, fieldType, features);
} else {
ObjectWriter fieldValueWriter = fieldWriter.getObjectWriter(jsonWriter, fieldValue.getClass());
fieldValueWriter.write(jsonWriter, fieldValue, fieldName, fieldType, features);
}
}
}
}
AfterFilter afterFilter = context.getAfterFilter();
if (afterFilter != null) {
afterFilter.writeAfter(jsonWriter, object);
}
jsonWriter.endObject();
}
public JSONObject toJSONObject(T object) {
return toJSONObject(object, 0);
}
public JSONObject toJSONObject(T object, long features) {
JSONObject jsonObject = new JSONObject();
for (int i = 0, size = fieldWriters.size(); i < size; i++) {
FieldWriter fieldWriter = fieldWriters.get(i);
Object fieldValue = fieldWriter.getFieldValue(object);
String format = fieldWriter.format;
Class fieldClass = fieldWriter.fieldClass;
if (format != null) {
if (fieldClass == Date.class) {
if ("millis".equals(format)) {
fieldValue = ((Date) fieldValue).getTime();
} else {
fieldValue = DateUtils.format((Date) fieldValue, format);
}
} else if (fieldClass == LocalDate.class) {
fieldValue = DateUtils.format((LocalDate) fieldValue, format);
} else if (fieldClass == LocalDateTime.class) {
fieldValue = DateUtils.format((LocalDateTime) fieldValue, format);
}
}
long fieldFeatures = fieldWriter.features;
if ((fieldFeatures & FieldInfo.UNWRAPPED_MASK) != 0) {
if (fieldValue instanceof Map) {
jsonObject.putAll((Map) fieldValue);
continue;
}
ObjectWriter fieldObjectWriter = fieldWriter.getInitWriter();
if (fieldObjectWriter == null) {
fieldObjectWriter = JSONFactory.getDefaultObjectWriterProvider().getObjectWriter(fieldClass);
}
List unwrappedFieldWriters = fieldObjectWriter.getFieldWriters();
for (int j = 0, unwrappedSize = unwrappedFieldWriters.size(); j < unwrappedSize; j++) {
FieldWriter unwrappedFieldWriter = unwrappedFieldWriters.get(j);
Object unwrappedFieldValue = unwrappedFieldWriter.getFieldValue(fieldValue);
jsonObject.put(unwrappedFieldWriter.fieldName, unwrappedFieldValue);
}
continue;
}
if (fieldValue != null) {
String fieldValueClassName = fieldValue.getClass().getName();
if (Collection.class.isAssignableFrom(fieldClass)
&& fieldValue.getClass() != JSONObject.class
&& !fieldValueClassName.equals("com.alibaba.fastjson.JSONObject")
) {
Collection collection = (Collection) fieldValue;
JSONArray array = new JSONArray(collection.size());
for (Object item : collection) {
Object itemJSON = item == object ? jsonObject : JSON.toJSON(item);
array.add(itemJSON);
}
fieldValue = array;
}
}
if (fieldValue == null && ((this.features | features) & WriteNulls.mask) == 0) {
continue;
}
if (fieldValue == object) {
fieldValue = jsonObject;
}
jsonObject.put(fieldWriter.fieldName, fieldValue);
}
return jsonObject;
}
@Override
public String toString() {
return objectClass.getName();
}
protected void errorOnNoneSerializable() {
throw new JSONException("not support none serializable class " + objectClass.getName());
}
}