ObjectReader.java
package com.alibaba.fastjson2.reader;
import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.util.Fnv;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
/**
* ObjectReader is responsible for deserializing JSON data into Java objects.
* It provides a set of methods for creating object instances, reading JSON data,
* and handling field mapping.
*
* <p>This interface supports various features including:
* <ul>
* <li>Object creation with different parameter types</li>
* <li>JSON object and array reading</li>
* <li>Field mapping and value setting</li>
* <li>Auto-type support for polymorphic deserialization</li>
* <li>Custom feature configuration</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get an ObjectReader for a specific type
* ObjectReader<User> reader = JSONFactory.getDefaultObjectReaderProvider().getObjectReader(User.class);
*
* // Read from JSON string
* String jsonString = "{\"id\":1,\"name\":\"John\"}";
* User user = reader.readObject(JSONReader.of(jsonString));
*
* // Create instance with map data
* Map<String, Object> data = new HashMap<>();
* data.put("id", 1);
* data.put("name", "John");
* User user2 = reader.createInstance(data);
* </pre>
*
* @param <T> the type of objects that this ObjectReader can deserialize
* @since 2.0.0
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public interface ObjectReader<T> {
long HASH_TYPE = Fnv.hashCode64("@type");
String VALUE_NAME = "@value";
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
*/
default T createInstance() {
return createInstance(0);
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
*/
default T createInstance(long features) {
throw new UnsupportedOperationException();
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
*/
default T createInstance(Collection collection) {
return createInstance(collection, 0L);
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
*/
default T createInstance(Collection collection, JSONReader.Feature... features) {
return createInstance(collection, JSONReader.Feature.of(features));
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
*/
default T createInstance(Collection collection, long features) {
throw new UnsupportedOperationException(this.getClass().getName());
}
/**
* Accepts extra field data that is not mapped to any specific field in the object.
* This method is called when deserializing JSON objects that contain fields not
* present in the target class.
*
* @param object the object being deserialized
* @param fieldName the name of the extra field
* @param fieldValue the value of the extra field
*/
default void acceptExtra(Object object, String fieldName, Object fieldValue) {
acceptExtra(object, fieldName, fieldValue, this.getFeatures());
}
/**
* Accepts extra field data that is not mapped to any specific field in the object.
* This method is called when deserializing JSON objects that contain fields not
* present in the target class.
*
* @param object the object being deserialized
* @param fieldName the name of the extra field
* @param fieldValue the value of the extra field
* @param features the JSON reader features to use
*/
default void acceptExtra(Object object, String fieldName, Object fieldValue, long features) {
}
/**
* Creates an instance of the object type from a map of field values using the specified features.
*
* @param map the map containing field names and values
* @param features the JSON reader features to use
* @return a new instance of the object populated with values from the map
*/
default T createInstance(Map map, JSONReader.Feature... features) {
long featuresValue = 0;
for (int i = 0; i < features.length; i++) {
featuresValue |= features[i].mask;
}
return createInstance(map, featuresValue);
}
/**
* @return {@link T}
* @throws JSONException If a suitable ObjectReader is not found
*/
default T createInstance(Map map, long features) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
Object typeKey = map.get(getTypeKey());
if (typeKey instanceof String) {
String typeName = (String) typeKey;
long typeHash = Fnv.hashCode64(typeName);
ObjectReader<T> reader = null;
if ((features & JSONReader.Feature.SupportAutoType.mask) != 0 || this instanceof ObjectReaderSeeAlso) {
reader = autoType(provider, typeHash);
}
if (reader == null) {
reader = provider.getObjectReader(
typeName, getObjectClass(), features | getFeatures()
);
}
if (reader != this && reader != null) {
return reader.createInstance(map, features);
}
}
T object = createInstance(0L);
return accept(object, map, features);
}
/**
* Accepts field values from a map and populates the specified object instance.
*
* @param object the object instance to populate
* @param map the map containing field names and values
* @param features the JSON reader features to use
* @return the populated object instance
*/
default T accept(T object, Map map, long features) {
for (Map.Entry entry : (Iterable<Map.Entry>) map.entrySet()) {
String entryKey = entry.getKey().toString();
Object fieldValue = entry.getValue();
FieldReader fieldReader = getFieldReader(entryKey);
if (fieldReader == null) {
acceptExtra(object, entryKey, entry.getValue(), features);
continue;
}
fieldReader.acceptAny(object, fieldValue, features);
}
Function buildFunction = getBuildFunction();
return buildFunction != null
? (T) buildFunction.apply(object)
: object;
}
/**
* Creates an instance of the object type using a non-default constructor with the specified values.
*
* @param values the map of field hash codes to values
* @return a new instance of the object created with a non-default constructor
* @throws UnsupportedOperationException if the method is not overloaded or otherwise
*/
default T createInstanceNoneDefaultConstructor(Map<Long, Object> values) {
throw new UnsupportedOperationException();
}
/**
* Gets the features enabled by this ObjectReader.
*
* @return the enabled features as a bit mask
*/
default long getFeatures() {
return 0L;
}
/**
* Gets the type key used for auto-type support. This key is used to identify
* the type information in JSON objects.
*
* @return the type key string
*/
default String getTypeKey() {
return "@type";
}
/**
* Gets the hash code of the type key used for auto-type support.
*
* @return the hash code of the type key
*/
default long getTypeKeyHash() {
return HASH_TYPE;
}
/**
* Gets the class of objects that this ObjectReader can deserialize.
*
* @return the object class, or null if not specified
*/
default Class<T> getObjectClass() {
return null;
}
/**
* Gets the FieldReader for the specified field hash code.
*
* @param hashCode the hash code of the field name
* @return the FieldReader for the field, or null if not found
*/
default FieldReader getFieldReader(long hashCode) {
return null;
}
/**
* Gets the FieldReader for the specified field hash code, using lowercase matching.
*
* @param hashCode the hash code of the field name (lowercase)
* @return the FieldReader for the field, or null if not found
*/
default FieldReader getFieldReaderLCase(long hashCode) {
return null;
}
/**
* Sets the value of a field in the specified object.
*
* @param object the object in which to set the field value
* @param fieldName the name of the field to set
* @param fieldNameHashCode the hash code of the field name
* @param value the integer value to set
* @return true if the field was successfully set, false otherwise
*/
default boolean setFieldValue(Object object, String fieldName, long fieldNameHashCode, int value) {
FieldReader fieldReader = getFieldReader(fieldNameHashCode);
if (fieldReader == null) {
return false;
}
fieldReader.accept(object, value);
return true;
}
/**
* Sets the value of a field in the specified object.
*
* @param object the object in which to set the field value
* @param fieldName the name of the field to set
* @param fieldNameHashCode the hash code of the field name
* @param value the long value to set
* @return true if the field was successfully set, false otherwise
*/
default boolean setFieldValue(Object object, String fieldName, long fieldNameHashCode, long value) {
FieldReader fieldReader = getFieldReader(fieldNameHashCode);
if (fieldReader == null) {
return false;
}
fieldReader.accept(object, value);
return true;
}
/**
* Gets the FieldReader for the specified field name.
*
* @param fieldName the name of the field
* @return the FieldReader for the field, or null if not found
*/
default FieldReader getFieldReader(String fieldName) {
long fieldNameHash = Fnv.hashCode64(fieldName);
FieldReader fieldReader = getFieldReader(fieldNameHash);
if (fieldReader == null) {
fieldReader = getFieldReaderLCase(fieldNameHash);
if (fieldReader == null) {
long fieldNameHashLCase = Fnv.hashCode64LCase(fieldName);
if (fieldNameHashLCase != fieldNameHash) {
fieldReader = getFieldReaderLCase(fieldNameHashLCase);
}
}
}
return fieldReader;
}
/**
* Sets the value of a field in the specified object.
*
* @param object the object in which to set the field value
* @param fieldName the name of the field to set
* @param value the object value to set
* @return true if the field was successfully set, false otherwise
*/
default boolean setFieldValue(Object object, String fieldName, Object value) {
FieldReader fieldReader = getFieldReader(fieldName);
if (fieldReader == null) {
return false;
}
fieldReader.accept(object, value);
return true;
}
/**
* Gets the build function used to construct the final object instance.
*
* @return the build function, or null if not specified
*/
default Function getBuildFunction() {
return null;
}
/**
* Resolves an ObjectReader for the specified type hash using the JSON reader context.
*
* @param context the JSON reader context
* @param typeHash the hash code of the type name
* @return the ObjectReader for the type, or null if not found
*/
default ObjectReader autoType(JSONReader.Context context, long typeHash) {
return context.getObjectReaderAutoType(typeHash);
}
/**
* Resolves an ObjectReader for the specified type hash using the ObjectReaderProvider.
*
* @param provider the ObjectReaderProvider
* @param typeHash the hash code of the type name
* @return the ObjectReader for the type, or null if not found
*/
default ObjectReader autoType(ObjectReaderProvider provider, long typeHash) {
return provider.getObjectReader(typeHash);
}
/**
* Reads an object from JSONB format.
*
* @param jsonReader the JSONReader to use for parsing
* @param fieldType the type of the field being read
* @param fieldName the name of the field being read
* @param features the features to use
* @return the deserialized object
* @throws JSONException if a suitable ObjectReader is not found
*/
default T readJSONBObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
if (jsonReader.isArray() &&
jsonReader.isSupportBeanArray()) {
return readArrayMappingJSONBObject(jsonReader, fieldType, fieldName, features);
}
T object = null;
jsonReader.nextIfObjectStart();
JSONReader.Context context = jsonReader.getContext();
long features2 = context.getFeatures() | features;
for (int i = 0; ; ++i) {
if (jsonReader.nextIfObjectEnd()) {
break;
}
long hash = jsonReader.readFieldNameHashCode();
if (hash == getTypeKeyHash() && i == 0) {
long typeHash = jsonReader.readTypeHashCode();
ObjectReader reader = autoType(context, typeHash);
if (reader == null) {
String typeName = jsonReader.getString();
reader = context.getObjectReaderAutoType(typeName, null);
if (reader == null) {
throw new JSONException(jsonReader.info("No suitable ObjectReader found for " + typeName));
}
}
if (reader == this) {
continue;
}
return (T) reader.readJSONBObject(jsonReader, fieldType, fieldName, features);
}
if (hash == 0) {
continue;
}
FieldReader fieldReader = getFieldReader(hash);
if (fieldReader == null && jsonReader.isSupportSmartMatch(features2 | getFeatures())) {
long nameHashCodeLCase = jsonReader.getNameHashCodeLCase();
if (nameHashCodeLCase != hash) {
fieldReader = getFieldReaderLCase(nameHashCodeLCase);
}
}
if (fieldReader == null) {
jsonReader.skipValue();
continue;
}
if (object == null) {
object = createInstance(features2);
}
fieldReader.readFieldValue(jsonReader, object);
}
return object != null
? object
: createInstance(features2);
}
/**
* Reads an object from JSONB format with array mapping.
*
* @param jsonReader the JSONReader to use for parsing
* @param fieldType the type of the field being read
* @param fieldName the name of the field being read
* @param features the features to use
* @return the deserialized object
* @throws UnsupportedOperationException if the method is not overloaded or otherwise
*/
default T readArrayMappingJSONBObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
throw new UnsupportedOperationException();
}
/**
* Reads an object from JSON format with array mapping.
*
* @param jsonReader the JSONReader to use for parsing
* @param fieldType the type of the field being read
* @param fieldName the name of the field being read
* @param features the features to use
* @return the deserialized object
* @throws UnsupportedOperationException if the method is not overloaded or otherwise
*/
default T readArrayMappingObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
throw new UnsupportedOperationException();
}
/**
* Reads an object from the specified JSON string using the provided features.
*
* @param str the JSON string to parse
* @param features the JSON reader features to use
* @return the deserialized object
*/
default T readObject(String str, JSONReader.Feature... features) {
try (JSONReader jsonReader = JSONReader.of(str, JSONFactory.createReadContext(features))) {
return readObject(jsonReader, null, null, getFeatures());
}
}
/**
* Reads an object from the specified JSONReader.
*
* @param jsonReader the JSONReader to use for parsing
* @return the deserialized object
*/
default T readObject(JSONReader jsonReader) {
return readObject(jsonReader, null, null, getFeatures());
}
/**
* Reads an object from the specified JSONReader using the provided features.
*
* @param jsonReader the JSONReader to use for parsing
* @param features the features to use
* @return the deserialized object
*/
default T readObject(JSONReader jsonReader, long features) {
return readObject(jsonReader, null, null, features);
}
/**
* Reads an object from the specified JSONReader with the given field type, field name, and features.
*
* @param jsonReader the JSONReader to use for parsing
* @param fieldType the type of the field being read
* @param fieldName the name of the field being read
* @param features the features to use
* @return the deserialized object
* @throws JSONException if a suitable ObjectReader is not found
*/
T readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features);
}