This commit is contained in:
wenshao 2025-08-18 15:36:38 +08:00
parent 559ba1d777
commit 74018e4f19
24 changed files with 10107 additions and 151 deletions

View File

@ -33,6 +33,77 @@ import static com.alibaba.fastjson2.JSONReader.EOI;
import static com.alibaba.fastjson2.JSONReader.Feature.IgnoreCheckClose;
import static com.alibaba.fastjson2.JSONReader.Feature.UseNativeObject;
/**
* This is the main entry point for using fastjson2 API.
* It provides a set of static methods for JSON processing,
* including parsing and serialization of various data types.
*
* <p>Example usage:
* <pre>
* // 1. Parse JSON string to Object
* String jsonString = "{\"id\":1,\"name\":\"John\",\"age\":30}";
* User user = JSON.parseObject(jsonString, User.class);
*
* // 2. Serialize Object to JSON string
* User user = new User(1L, "John", 30, new Date(), Arrays.asList("reading", "swimming"));
* String jsonString = JSON.toJSONString(user);
*
* // 3. Parse JSON string to JSONObject
* JSONObject jsonObject = JSON.parseObject(jsonString);
*
* // 4. Parse JSON string to JSONArray
* String jsonArrayString = "[{\"id\":1,\"name\":\"John\"},{\"id\":2,\"name\":\"Jane\"}]";
* JSONArray jsonArray = JSON.parseArray(jsonArrayString);
*
* // 5. Parse JSON string to List
* List<User> userList = JSON.parseArray(jsonArrayString, User.class);
*
* // 6. Parse JSON with features
* User user = JSON.parseObject(jsonString, User.class, JSONReader.Feature.FieldBased);
*
* // 7. Serialize with features
* String jsonString = JSON.toJSONString(user, JSONWriter.Feature.PrettyFormat);
*
* // 8. Parse from byte array
* byte[] bytes = jsonString.getBytes(StandardCharsets.UTF_8);
* User user = JSON.parseObject(bytes, User.class);
*
* // 9. Serialize to byte array
* byte[] bytes = JSON.toJSONBytes(user);
*
* // 10. Validate JSON string
* boolean valid = JSON.isValid(jsonString);
* </pre>
*
* <p>For more advanced usage:
* <pre>
* // 1. Working with generic types
* String jsonString = "{\"users\":[{\"id\":1,\"name\":\"John\"}]}";
* TypeReference<Map<String, List<User>>> typeReference = new TypeReference<Map<String, List<User>>>() {};
* Map<String, List<User>> map = JSON.parseObject(jsonString, typeReference);
*
* // 2. Working with filters
* SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
* filter.getExcludes().add("id");
* String jsonString = JSON.toJSONString(user, filter);
*
* // 3. Working with date format
* String jsonString = JSON.toJSONString(user, "yyyy-MM-dd HH:mm:ss");
*
* // 4. Working with custom context
* JSONReader.Context readerContext = JSONFactory.createReadContext();
* readerContext.setDateFormat("yyyy-MM-dd");
* User user = JSON.parseObject(jsonString, User.class, readerContext);
*
* // 5. Streaming parsing
* try (InputStream inputStream = new FileInputStream("data.json")) {
* JSON.parseObject(inputStream, User.class, System.out::println);
* }
* </pre>
*
* @since 2.0.0
*/
public interface JSON {
/**
* fastjson2 version name
@ -3586,6 +3657,7 @@ public interface JSON {
* @param features the specified features is applied to serialization
* @return the length of byte stream
* @throws JSONException If an I/O error or serialization error occurs
* @since 2.0.2
*/
static int writeTo(OutputStream out, Object object, JSONWriter.Feature... features) {
final JSONWriter.Context context = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider, features);
@ -3616,6 +3688,7 @@ public interface JSON {
* @param features the specified features is applied to serialization
* @return the length of byte stream
* @throws JSONException If an I/O error or serialization error occurs
* @since 2.0.2
*/
static int writeTo(OutputStream out, Object object, Filter[] filters, JSONWriter.Feature... features) {
final JSONWriter.Context context = new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider, features);
@ -3651,6 +3724,7 @@ public interface JSON {
* @param features the specified features is applied to serialization
* @return the length of byte stream
* @throws JSONException If an I/O error or serialization error occurs
* @since 2.0.2
*/
static int writeTo(
OutputStream out,
@ -3690,6 +3764,7 @@ public interface JSON {
*
* @param text the specified string will be validated
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValid(String text) {
if (text == null || text.isEmpty()) {
@ -3710,6 +3785,7 @@ public interface JSON {
* @param text the specified string will be validated
* @param features the specified features is applied to parsing
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValid(String text, JSONReader.Feature... features) {
if (text == null || text.isEmpty()) {
@ -3729,6 +3805,7 @@ public interface JSON {
*
* @param chars the specified array will be validated
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValid(char[] chars) {
if (chars == null || chars.length == 0) {
@ -3748,6 +3825,7 @@ public interface JSON {
*
* @param text the specified string will be validated
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValidObject(String text) {
if (text == null || text.isEmpty()) {
@ -3770,6 +3848,7 @@ public interface JSON {
*
* @param bytes the specified array will be validated
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValidObject(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
@ -3792,6 +3871,7 @@ public interface JSON {
*
* @param text the {@link String} to validate
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValidArray(String text) {
if (text == null || text.isEmpty()) {
@ -3814,6 +3894,7 @@ public interface JSON {
*
* @param bytes the specified array will be validated
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValid(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
@ -3834,6 +3915,7 @@ public interface JSON {
* @param bytes the specified array will be validated
* @param charset the specified charset of the bytes
* @return {@code true} or {@code false}
* @since 2.0.2
*/
static boolean isValid(byte[] bytes, Charset charset) {
if (bytes == null || bytes.length == 0) {
@ -3940,8 +4022,10 @@ public interface JSON {
/**
* Converts the specified object to an object of the specified goal type
*
* @param <T> the target type
* @param clazz the specified goal class
* @param object the specified object to be converted
* @return the converted object of type T, or null if the input object is null
* @since 2.0.4
*/
static <T> T to(Class<T> clazz, Object object) {
@ -3959,8 +4043,10 @@ public interface JSON {
/**
* Converts the specified object to an object of the specified goal type
*
* @param clazz the specified goal class
* @param <T> the target type
* @param object the specified object to be converted
* @param clazz the specified goal class
* @return the converted object of type T, or null if the input object is null
* @deprecated since 2.0.4, please use {@link #to(Class, Object)}
*/
static <T> T toJavaObject(Object object, Class<T> clazz) {
@ -3968,6 +4054,10 @@ public interface JSON {
}
/**
* Mixes in the properties of the mixinSource class into the target class
*
* @param target the target class to mix into
* @param mixinSource the source class to mix from
* @since 2.0.2
*/
static void mixIn(Class<?> target, Class<?> mixinSource) {
@ -3978,6 +4068,9 @@ public interface JSON {
/**
* Register an {@link ObjectReader} for {@link Type} in default {@link com.alibaba.fastjson2.reader.ObjectReaderProvider}
*
* @param type the type to register an ObjectReader for
* @param objectReader the ObjectReader to register
* @return the previously registered ObjectReader, or null if there was none
* @see JSONFactory#getDefaultObjectReaderProvider()
* @see com.alibaba.fastjson2.reader.ObjectReaderProvider#register(Type, ObjectReader)
* @since 2.0.2
@ -3989,6 +4082,10 @@ public interface JSON {
/**
* Register an {@link ObjectReader} for {@link Type} in default {@link com.alibaba.fastjson2.reader.ObjectReaderProvider}
*
* @param type the type to register an ObjectReader for
* @param objectReader the ObjectReader to register
* @param fieldBased whether to use field-based reading
* @return the previously registered ObjectReader, or null if there was none
* @see JSONFactory#getDefaultObjectReaderProvider()
* @see com.alibaba.fastjson2.reader.ObjectReaderProvider#register(Type, ObjectReader)
* @since 2.0.38
@ -4000,6 +4097,9 @@ public interface JSON {
/**
* Register if absent an {@link ObjectReader} for {@link Type} in default {@link com.alibaba.fastjson2.reader.ObjectReaderProvider}
*
* @param type the type to register an ObjectReader for
* @param objectReader the ObjectReader to register
* @return the previously registered ObjectReader, or null if there was none
* @see JSONFactory#getDefaultObjectReaderProvider()
* @see com.alibaba.fastjson2.reader.ObjectReaderProvider#registerIfAbsent(Type, ObjectReader)
* @since 2.0.6
@ -4011,6 +4111,10 @@ public interface JSON {
/**
* Register if absent an {@link ObjectReader} for {@link Type} in default {@link com.alibaba.fastjson2.reader.ObjectReaderProvider}
*
* @param type the type to register an ObjectReader for
* @param objectReader the ObjectReader to register
* @param fieldBased whether to use field-based reading
* @return the previously registered ObjectReader, or null if there was none
* @see JSONFactory#getDefaultObjectReaderProvider()
* @see com.alibaba.fastjson2.reader.ObjectReaderProvider#registerIfAbsent(Type, ObjectReader)
* @since 2.0.38
@ -4022,18 +4126,34 @@ public interface JSON {
/**
* Register an {@link ObjectReaderModule} in default {@link com.alibaba.fastjson2.reader.ObjectReaderProvider}
*
* @param objectReaderModule the ObjectReaderModule to register
* @return true if the module was registered successfully, false otherwise
* @see JSONFactory#getDefaultObjectReaderProvider()
* @see com.alibaba.fastjson2.reader.ObjectReaderProvider#register(ObjectReaderModule)
* @since 2.0.2
*/
static boolean register(ObjectReaderModule objectReaderModule) {
ObjectReaderProvider provider = getDefaultObjectReaderProvider();
return provider.register(objectReaderModule);
}
/**
* Register a see-also sub type
*
* @param subTypeClass the sub type class to register
* @since 2.0.2
*/
static void registerSeeAlsoSubType(Class subTypeClass) {
registerSeeAlsoSubType(subTypeClass, null);
}
/**
* Register a see-also sub type with a specific class name
*
* @param subTypeClass the sub type class to register
* @param subTypeClassName the class name for the sub type
* @since 2.0.2
*/
static void registerSeeAlsoSubType(Class subTypeClass, String subTypeClassName) {
ObjectReaderProvider provider = getDefaultObjectReaderProvider();
provider.registerSeeAlsoSubType(subTypeClass, subTypeClassName);
@ -4042,8 +4162,11 @@ public interface JSON {
/**
* Register an {@link ObjectWriterModule} in default {@link com.alibaba.fastjson2.writer.ObjectWriterProvider}
*
* @param objectWriterModule the ObjectWriterModule to register
* @return true if the module was registered successfully, false otherwise
* @see JSONFactory#getDefaultObjectWriterProvider()
* @see com.alibaba.fastjson2.writer.ObjectWriterProvider#register(ObjectWriterModule)
* @since 2.0.2
*/
static boolean register(ObjectWriterModule objectWriterModule) {
return JSONFactory.getDefaultObjectWriterProvider().register(objectWriterModule);
@ -4052,6 +4175,9 @@ public interface JSON {
/**
* Register an {@link ObjectWriter} for {@link Type} in default {@link com.alibaba.fastjson2.writer.ObjectWriterProvider}
*
* @param type the type to register an ObjectWriter for
* @param objectWriter the ObjectWriter to register
* @return the previously registered ObjectWriter, or null if there was none
* @see JSONFactory#getDefaultObjectWriterProvider()
* @see com.alibaba.fastjson2.writer.ObjectWriterProvider#register(Type, ObjectWriter)
* @since 2.0.2
@ -4063,6 +4189,10 @@ public interface JSON {
/**
* Register an {@link ObjectWriter} for {@link Type} in default {@link com.alibaba.fastjson2.writer.ObjectWriterProvider}
*
* @param type the type to register an ObjectWriter for
* @param objectWriter the ObjectWriter to register
* @param fieldBased whether to use field-based writing
* @return the previously registered ObjectWriter, or null if there was none
* @see JSONFactory#getDefaultObjectWriterProvider()
* @see com.alibaba.fastjson2.writer.ObjectWriterProvider#register(Type, ObjectWriter)
* @since 2.0.38
@ -4074,6 +4204,9 @@ public interface JSON {
/**
* Register if absent an {@link ObjectWriter} for {@link Type} in default {@link com.alibaba.fastjson2.writer.ObjectWriterProvider}
*
* @param type the type to register an ObjectWriter for
* @param objectWriter the ObjectWriter to register
* @return the previously registered ObjectWriter, or null if there was none
* @see JSONFactory#getDefaultObjectWriterProvider()
* @see com.alibaba.fastjson2.writer.ObjectWriterProvider#registerIfAbsent(Type, ObjectWriter)
* @since 2.0.6
@ -4085,6 +4218,10 @@ public interface JSON {
/**
* Register if absent an {@link ObjectWriter} for {@link Type} in default {@link com.alibaba.fastjson2.writer.ObjectWriterProvider}
*
* @param type the type to register an ObjectWriter for
* @param objectWriter the ObjectWriter to register
* @param fieldBased whether to use field-based writing
* @return the previously registered ObjectWriter, or null if there was none
* @see JSONFactory#getDefaultObjectWriterProvider()
* @see com.alibaba.fastjson2.writer.ObjectWriterProvider#registerIfAbsent(Type, ObjectWriter)
* @since 2.0.6
@ -4096,8 +4233,8 @@ public interface JSON {
/**
* Register ObjectWriterFilter
*
* @param type
* @param filter
* @param type the class type to register filter for
* @param filter the filter to apply to the specified type
* @since 2.0.19
*/
static void register(Class type, Filter filter) {
@ -4167,9 +4304,9 @@ public interface JSON {
}
/**
* config default reader dateFormat
* Config default reader dateFormat
*
* @param dateFormat
* @param dateFormat the date format to use for reading
* @since 2.0.30
*/
static void configReaderDateFormat(String dateFormat) {
@ -4177,18 +4314,19 @@ public interface JSON {
}
/**
* config default reader dateFormat
* Config default writer dateFormat
*
* @param dateFormat
* @param dateFormat the date format to use for writing
* @since 2.0.30
*/
static void configWriterDateFormat(String dateFormat) {
defaultWriterFormat = dateFormat;
}
/**
* config default reader zoneId
* Config default reader zoneId
*
* @param zoneId
* @param zoneId the zone ID to use for reading
* @since 2.0.36
*/
static void configReaderZoneId(ZoneId zoneId) {
@ -4196,9 +4334,9 @@ public interface JSON {
}
/**
* config default writer zoneId
* Config default writer zoneId
*
* @param zoneId
* @param zoneId the zone ID to use for writing
* @since 2.0.36
*/
static void configWriterZoneId(ZoneId zoneId) {
@ -4245,8 +4383,10 @@ public interface JSON {
/**
* Builds a new {@link T} using the properties of the specified object
*
* @param <T> the type of the object to copy
* @param object the specified object will be copied
* @param features the specified features is applied to serialization
* @return a new instance of T with the same properties as the input object, or null if the input is null
* @since 2.0.12
*/
static <T> T copy(T object, JSONWriter.Feature... features) {
@ -4324,9 +4464,11 @@ public interface JSON {
/**
* Builds a new instance of targetClass using the properties of the specified object
*
* @param <T> the target type
* @param object the specified object will be copied
* @param targetClass the specified target class
* @param features the specified features is applied to serialization
* @return a new instance of targetClass with properties copied from the input object, or null if the input is null
* @since 2.0.16
*/
static <T> T copyTo(Object object, Class<T> targetClass, JSONWriter.Feature... features) {
@ -4416,8 +4558,9 @@ public interface JSON {
/**
* Configure the Enum classes as a JavaBean
*
* @param enumClasses the enum classes to configure as JavaBeans
* @since 2.0.55
* @param enumClasses enum classes
*/
@SuppressWarnings("rawtypes")
@SafeVarargs

View File

@ -863,6 +863,12 @@ public class JSONArray
}
/**
* Returns the {@link Date} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link Date} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.27
*/
public Date getDate(int index, Date defaultValue) {
@ -903,7 +909,11 @@ public class JSONArray
}
/**
* Returns the {@link LocalDate} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link LocalDate} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalDate getLocalDate(int index) {
@ -911,7 +921,12 @@ public class JSONArray
}
/**
* Returns the {@link LocalDate} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link LocalDate} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalDate getLocalDate(int index, LocalDate defaultValue) {
@ -926,7 +941,11 @@ public class JSONArray
}
/**
* Returns the {@link LocalTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link LocalTime} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalTime getLocalTime(int index) {
@ -934,7 +953,12 @@ public class JSONArray
}
/**
* Returns the {@link LocalTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link LocalTime} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalTime getLocalTime(int index, LocalTime defaultValue) {
@ -949,7 +973,11 @@ public class JSONArray
}
/**
* Returns the {@link OffsetTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link OffsetTime} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public OffsetTime getOffsetTime(int index) {
@ -957,7 +985,12 @@ public class JSONArray
}
/**
* Returns the {@link OffsetTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link OffsetTime} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public OffsetTime getOffsetTime(int index, OffsetTime defaultValue) {
@ -972,7 +1005,11 @@ public class JSONArray
}
/**
* Returns the {@link LocalDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link LocalDateTime} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalDateTime getLocalDateTime(int index) {
@ -980,7 +1017,12 @@ public class JSONArray
}
/**
* Returns the {@link LocalDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link LocalDateTime} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public LocalDateTime getLocalDateTime(int index, LocalDateTime defaultValue) {
@ -995,7 +1037,11 @@ public class JSONArray
}
/**
* Returns the {@link OffsetDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link OffsetDateTime} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public OffsetDateTime getOffsetDateTime(int index) {
@ -1003,7 +1049,12 @@ public class JSONArray
}
/**
* Returns the {@link OffsetDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link OffsetDateTime} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public OffsetDateTime getOffsetDateTime(int index, OffsetDateTime defaultValue) {
@ -1018,7 +1069,11 @@ public class JSONArray
}
/**
* Returns the {@link ZonedDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @return {@link ZonedDateTime} or null
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public ZonedDateTime getZonedDateTime(int index) {
@ -1026,7 +1081,12 @@ public class JSONArray
}
/**
* Returns the {@link ZonedDateTime} at the specified location in this {@link JSONArray}.
*
* @param index index of the element to return
* @param defaultValue default value to return if the element is null
* @return {@link ZonedDateTime} or defaultValue
* @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
* @since 2.0.57
*/
public ZonedDateTime getZonedDateTime(int index, ZonedDateTime defaultValue) {
@ -1461,12 +1521,24 @@ public class JSONArray
return creator.apply(object);
}
/**
* Adds a new {@link JSONObject} to the end of this {@link JSONArray}.
*
* @return the newly created {@link JSONObject}
* @since 2.0.3
*/
public JSONObject addObject() {
JSONObject object = new JSONObject();
add(object);
return object;
}
/**
* Adds a new {@link JSONArray} to the end of this {@link JSONArray}.
*
* @return the newly created {@link JSONArray}
* @since 2.0.3
*/
public JSONArray addArray() {
JSONArray array = new JSONArray();
add(array);
@ -1481,6 +1553,8 @@ public class JSONArray
* </pre>
*
* @param element element to be appended to this list
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentAdd(Object element) {
add(element);
@ -1488,6 +1562,9 @@ public class JSONArray
}
/**
* Chained clear operation that removes all elements from this {@link JSONArray}.
*
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentClear() {
@ -1496,6 +1573,10 @@ public class JSONArray
}
/**
* Chained remove operation that removes the element at the specified position.
*
* @param index the index of the element to be removed
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentRemove(int index) {
@ -1504,6 +1585,11 @@ public class JSONArray
}
/**
* Chained set operation that replaces the element at the specified position.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentSet(int index, Object element) {
@ -1512,6 +1598,10 @@ public class JSONArray
}
/**
* Chained remove operation that removes the first occurrence of the specified element.
*
* @param o element to be removed from this list, if present
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentRemove(Object o) {
@ -1520,6 +1610,10 @@ public class JSONArray
}
/**
* Chained remove operation that removes from this list all of its elements that are contained in the specified collection.
*
* @param c collection containing elements to be removed from this list
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentRemoveAll(Collection<?> c) {
@ -1528,6 +1622,10 @@ public class JSONArray
}
/**
* Chained add operation that appends all of the elements in the specified collection to the end of this list.
*
* @param c collection containing elements to be added to this list
* @return this {@link JSONArray} instance
* @since 2.0.3
*/
public JSONArray fluentAddAll(Collection<?> c) {
@ -1536,6 +1634,10 @@ public class JSONArray
}
/**
* Checks if this {@link JSONArray} is valid against the specified {@link JSONSchema}.
*
* @param schema the {@link JSONSchema} to validate against
* @return true if this {@link JSONArray} is valid against the schema, false otherwise
* @since 2.0.3
*/
public boolean isValid(JSONSchema schema) {
@ -1544,6 +1646,11 @@ public class JSONArray
.isSuccess();
}
/**
* Creates and returns a copy of this {@link JSONArray}.
*
* @return a clone of this instance
*/
@Override
public Object clone() {
return new JSONArray(this);

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,13 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Supplier;
/**
* JSONFactory is the core factory class for creating JSON readers and writers,
* as well as managing global configuration for fastjson2.
*
* @author wenshao
* @since 2.0.59
*/
public final class JSONFactory {
public static final class Conf {
static final Properties DEFAULT_PROPERTIES;
@ -397,7 +404,9 @@ public final class JSONFactory {
}
/**
* @param objectSupplier
* Sets the default object supplier used when creating JSON objects.
*
* @param objectSupplier the supplier for creating Map instances
* @since 2.0.15
*/
public static void setDefaultObjectSupplier(Supplier<Map> objectSupplier) {
@ -405,45 +414,92 @@ public final class JSONFactory {
}
/**
* @param arraySupplier
* Sets the default array supplier used when creating JSON arrays.
*
* @param arraySupplier the supplier for creating List instances
* @since 2.0.15
*/
public static void setDefaultArraySupplier(Supplier<List> arraySupplier) {
defaultArraySupplier = arraySupplier;
}
/**
* Gets the default object supplier used when creating JSON objects.
*
* @return the supplier for creating Map instances
*/
public static Supplier<Map> getDefaultObjectSupplier() {
return defaultObjectSupplier;
}
/**
* Gets the default array supplier used when creating JSON arrays.
*
* @return the supplier for creating List instances
*/
public static Supplier<List> getDefaultArraySupplier() {
return defaultArraySupplier;
}
/**
* Creates a new JSON writer context with default settings.
*
* @return a new JSONWriter.Context instance
*/
public static JSONWriter.Context createWriteContext() {
return new JSONWriter.Context(defaultObjectWriterProvider);
}
/**
* Creates a new JSON writer context with the specified provider and features.
*
* @param provider the object writer provider
* @param features the features to enable
* @return a new JSONWriter.Context instance
*/
public static JSONWriter.Context createWriteContext(ObjectWriterProvider provider, JSONWriter.Feature... features) {
JSONWriter.Context context = new JSONWriter.Context(provider);
context.config(features);
return context;
}
/**
* Creates a new JSON writer context with the specified features.
*
* @param features the features to enable
* @return a new JSONWriter.Context instance
*/
public static JSONWriter.Context createWriteContext(JSONWriter.Feature... features) {
return new JSONWriter.Context(defaultObjectWriterProvider, features);
}
/**
* Creates a new JSON reader context with default settings.
*
* @return a new JSONReader.Context instance
*/
public static JSONReader.Context createReadContext() {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
return new JSONReader.Context(provider);
}
/**
* Creates a new JSON reader context with the specified features.
*
* @param features the features to enable
* @return a new JSONReader.Context instance
*/
public static JSONReader.Context createReadContext(long features) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
return new JSONReader.Context(provider, features);
}
/**
* Creates a new JSON reader context with the specified features.
*
* @param features the features to enable
* @return a new JSONReader.Context instance
*/
public static JSONReader.Context createReadContext(JSONReader.Feature... features) {
JSONReader.Context context = new JSONReader.Context(
JSONFactory.getDefaultObjectReaderProvider()
@ -525,10 +581,20 @@ public final class JSONFactory {
.getObjectWriter(type, TypeUtils.getClass(type), JSONWriter.Feature.FieldBased.isEnabled(features));
}
/**
* Gets the default object writer provider.
*
* @return the default ObjectWriterProvider instance
*/
public static ObjectWriterProvider getDefaultObjectWriterProvider() {
return defaultObjectWriterProvider;
}
/**
* Gets the default object reader provider.
*
* @return the default ObjectReaderProvider instance
*/
public static ObjectReaderProvider getDefaultObjectReaderProvider() {
ObjectReaderProvider providerLocal = readerProviderLocal.get();
if (providerLocal != null) {
@ -538,6 +604,11 @@ public final class JSONFactory {
return defaultObjectReaderProvider;
}
/**
* Gets the default JSONPath compiler.
*
* @return the default JSONPathCompiler instance
*/
public static JSONPathCompiler getDefaultJSONPathCompiler() {
JSONPathCompiler compilerLocal = jsonPathCompilerLocal.get();
if (compilerLocal != null) {
@ -547,26 +618,56 @@ public final class JSONFactory {
return defaultJSONPathCompiler;
}
/**
* Sets the object reader creator for the current thread context.
*
* @param creator the ObjectReaderCreator to set
*/
public static void setContextReaderCreator(ObjectReaderCreator creator) {
readerCreatorLocal.set(creator);
}
/**
* Sets the object reader provider for the current thread context.
*
* @param creator the ObjectReaderProvider to set
*/
public static void setContextObjectReaderProvider(ObjectReaderProvider creator) {
readerProviderLocal.set(creator);
}
/**
* Gets the object reader creator for the current thread context.
*
* @return the ObjectReaderCreator for the current thread, or null if not set
*/
public static ObjectReaderCreator getContextReaderCreator() {
return readerCreatorLocal.get();
}
/**
* Sets the JSONPath compiler for the current thread context.
*
* @param compiler the JSONPathCompiler to set
*/
public static void setContextJSONPathCompiler(JSONPathCompiler compiler) {
jsonPathCompilerLocal.set(compiler);
}
/**
* Sets the object writer creator for the current thread context.
*
* @param creator the ObjectWriterCreator to set
*/
public static void setContextWriterCreator(ObjectWriterCreator creator) {
writerCreatorLocal.set(creator);
}
/**
* Gets the object writer creator for the current thread context.
*
* @return the ObjectWriterCreator for the current thread, or null if not set
*/
public static ObjectWriterCreator getContextWriterCreator() {
return writerCreatorLocal.get();
}
@ -575,86 +676,186 @@ public final class JSONFactory {
JSONPath compile(Class objectClass, JSONPath path);
}
/**
* Gets the default reader features.
*
* @return the default reader features as a long value
*/
public static long getDefaultReaderFeatures() {
return defaultReaderFeatures;
}
/**
* Gets the default reader zone ID.
*
* @return the default ZoneId for readers
*/
public static ZoneId getDefaultReaderZoneId() {
return defaultReaderZoneId;
}
/**
* Gets the default reader format string.
*
* @return the default format string for readers
*/
public static String getDefaultReaderFormat() {
return defaultReaderFormat;
}
/**
* Gets the default writer features.
*
* @return the default writer features as a long value
*/
public static long getDefaultWriterFeatures() {
return defaultWriterFeatures;
}
/**
* Gets the default writer zone ID.
*
* @return the default ZoneId for writers
*/
public static ZoneId getDefaultWriterZoneId() {
return defaultWriterZoneId;
}
/**
* Gets the default writer format string.
*
* @return the default format string for writers
*/
public static String getDefaultWriterFormat() {
return defaultWriterFormat;
}
/**
* Checks if the default writer uses alphabetic ordering.
*
* @return true if alphabetic ordering is enabled, false otherwise
*/
public static boolean isDefaultWriterAlphabetic() {
return defaultWriterAlphabetic;
}
/**
* Sets whether the default writer should use alphabetic ordering.
*
* @param defaultWriterAlphabetic true to enable alphabetic ordering, false to disable
*/
public static void setDefaultWriterAlphabetic(boolean defaultWriterAlphabetic) {
JSONFactory.defaultWriterAlphabetic = defaultWriterAlphabetic;
}
/**
* Checks if reference detection is disabled.
*
* @return true if reference detection is disabled, false otherwise
*/
public static boolean isDisableReferenceDetect() {
return disableReferenceDetect;
}
/**
* Checks if auto type support is disabled.
*
* @return true if auto type is disabled, false otherwise
*/
public static boolean isDisableAutoType() {
return disableAutoType;
}
/**
* Checks if JSONB format is disabled.
*
* @return true if JSONB is disabled, false otherwise
*/
public static boolean isDisableJSONB() {
return disableJSONB;
}
/**
* Checks if array mapping is disabled.
*
* @return true if array mapping is disabled, false otherwise
*/
public static boolean isDisableArrayMapping() {
return disableArrayMapping;
}
/**
* Sets whether reference detection should be disabled.
*
* @param disableReferenceDetect true to disable reference detection, false to enable
*/
public static void setDisableReferenceDetect(boolean disableReferenceDetect) {
defaultObjectWriterProvider.setDisableReferenceDetect(disableReferenceDetect);
defaultObjectReaderProvider.setDisableReferenceDetect(disableReferenceDetect);
}
/**
* Sets whether array mapping should be disabled.
*
* @param disableArrayMapping true to disable array mapping, false to enable
*/
public static void setDisableArrayMapping(boolean disableArrayMapping) {
defaultObjectWriterProvider.setDisableArrayMapping(disableArrayMapping);
defaultObjectReaderProvider.setDisableArrayMapping(disableArrayMapping);
}
/**
* Sets whether JSONB format should be disabled.
*
* @param disableJSONB true to disable JSONB, false to enable
*/
public static void setDisableJSONB(boolean disableJSONB) {
defaultObjectWriterProvider.setDisableJSONB(disableJSONB);
defaultObjectReaderProvider.setDisableJSONB(disableJSONB);
}
/**
* Sets whether auto type support should be disabled.
*
* @param disableAutoType true to disable auto type, false to enable
*/
public static void setDisableAutoType(boolean disableAutoType) {
defaultObjectWriterProvider.setDisableAutoType(disableAutoType);
defaultObjectReaderProvider.setDisableAutoType(disableAutoType);
}
/**
* Checks if smart matching is disabled.
*
* @return true if smart matching is disabled, false otherwise
*/
public static boolean isDisableSmartMatch() {
return disableSmartMatch;
}
/**
* Sets whether smart matching should be disabled.
*
* @param disableSmartMatch true to disable smart matching, false to enable
*/
public static void setDisableSmartMatch(boolean disableSmartMatch) {
defaultObjectReaderProvider.setDisableSmartMatch(disableSmartMatch);
}
/**
* Checks if transient fields are skipped by default.
*
* @return true if transient fields are skipped, false otherwise
*/
public static boolean isDefaultSkipTransient() {
return defaultSkipTransient;
}
/**
* Sets whether transient fields should be skipped by default.
*
* @param skipTransient true to skip transient fields, false to include them
*/
public static void setDefaultSkipTransient(boolean skipTransient) {
defaultObjectWriterProvider.setSkipTransient(skipTransient);
}

View File

@ -4,13 +4,26 @@ package com.alibaba.fastjson2;
* Exception thrown when attempting to serialize an object that exceeds size limits
* and the LargeObject feature is not enabled.
*
* @author wenshao
* @since 2.0.58
*/
public class JSONLargeObjectException
extends JSONException {
/**
* Constructs a new JSONLargeObjectException with the specified detail message.
*
* @param message the detail message
*/
public JSONLargeObjectException(String message) {
super(message);
}
/**
* Constructs a new JSONLargeObjectException with the specified detail message and cause.
*
* @param message the detail message
* @param cause the cause of the exception
*/
public JSONLargeObjectException(String message, Throwable cause) {
super(message, cause);
}

View File

@ -184,9 +184,11 @@ public class JSONObject
}
/**
* Iterates over the JSONArray elements associated with the given key.
*
* @param key the key whose associated JSONArray is to be iterated
* @param action the action to be performed for each JSONObject element
* @since 2.0.52
* @param key
* @param action
* @deprecated Typo in the method name. Use {@link #forEachArrayObject(String, Consumer) forEachArrayObject} instead
*/
@Deprecated
@ -195,8 +197,10 @@ public class JSONObject
}
/**
* @param key
* @param action
* Iterates over the JSONArray elements associated with the given key.
*
* @param key the key whose associated JSONArray is to be iterated
* @param action the action to be performed for each JSONObject element
*/
public void forEachArrayObject(String key, Consumer<JSONObject> action) {
JSONArray array = getJSONArray(key);
@ -274,6 +278,15 @@ public class JSONObject
return null;
}
/**
* Returns a list of objects of the specified type from the associated JSONArray in this {@link JSONObject}.
*
* @param <T> the type of elements in the list
* @param key the key whose associated value is to be returned
* @param itemClass the class of the items in the list
* @param features features to be enabled in parsing
* @return a list of objects or null
*/
public <T> List<T> getList(String key, Class<T> itemClass, JSONReader.Feature... features) {
JSONArray jsonArray = getJSONArray(key);
if (jsonArray == null) {
@ -1532,14 +1545,18 @@ public class JSONObject
}
/**
* Handles method invocations on a proxy instance.
*
* @param proxy proxy object, currently useless
* @param method methods that need reflection
* @param args parameters of invoke
* @return the result of the method invocation
* @throws Throwable if an error occurs during method invocation
* @throws UnsupportedOperationException If reflection for this method is not supported
* @throws ArrayIndexOutOfBoundsException If the length of args does not match the length of the method parameter
*/
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final String methodName = method.getName();
int parameterCount = method.getParameterCount();
@ -1685,6 +1702,10 @@ public class JSONObject
}
/**
* Gets the JSON field name from the method's annotations.
*
* @param method the method to get the JSON field name from
* @return the JSON field name, or null if not found
* @since 2.0.4
*/
private String getJSONFieldName(Method method) {
@ -1709,12 +1730,24 @@ public class JSONObject
return name;
}
/**
* Creates and puts a new JSONArray with the specified name.
*
* @param name the name for the new JSONArray
* @return the created JSONArray
*/
public JSONArray putArray(String name) {
JSONArray array = new JSONArray();
put(name, array);
return array;
}
/**
* Creates and puts a new JSONObject with the specified name.
*
* @param name the name for the new JSONObject
* @return the created JSONObject
*/
public JSONObject putObject(String name) {
JSONObject object = new JSONObject();
put(name, object);
@ -1722,6 +1755,8 @@ public class JSONObject
}
/**
* Consumer for processing method names.
*
* @since 2.0.3
*/
static class NameConsumer
@ -1765,6 +1800,10 @@ public class JSONObject
}
/**
* Checks if this JSONObject is valid according to the specified JSON schema.
*
* @param schema the JSON schema to validate against
* @return true if this JSONObject is valid according to the schema, false otherwise
* @since 2.0.4
*/
public boolean isValid(JSONSchema schema) {
@ -1772,6 +1811,10 @@ public class JSONObject
}
/**
* Applies a name filter to an iterable collection.
*
* @param iterable the iterable collection to apply the filter to
* @param nameFilter the name filter to apply
* @since 2.0.3
*/
static void nameFilter(Iterable<?> iterable, NameFilter nameFilter) {
@ -1785,9 +1828,13 @@ public class JSONObject
}
/**
* Applies a name filter to a map.
*
* @param map the map to apply the filter to
* @param nameFilter the name filter to apply
* @since 2.0.3
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({ "rawtypes", "unchecked" })
static void nameFilter(Map map, NameFilter nameFilter) {
JSONObject changed = null;
for (Iterator<?> it = map.entrySet().iterator(); it.hasNext(); ) {
@ -1819,6 +1866,10 @@ public class JSONObject
}
/**
* Applies a value filter to an iterable collection.
*
* @param iterable the iterable collection to apply the filter to
* @param valueFilter the value filter to apply
* @since 2.0.3
*/
@SuppressWarnings("rawtypes")
@ -1833,9 +1884,13 @@ public class JSONObject
}
/**
* Applies a value filter to a map.
*
* @param map the map to apply the filter to
* @param valueFilter the value filter to apply
* @since 2.0.3
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({ "rawtypes", "unchecked" })
static void valueFilter(Map map, ValueFilter valueFilter) {
for (Object o : map.entrySet()) {
Map.Entry entry = (Map.Entry) o;
@ -1859,6 +1914,9 @@ public class JSONObject
}
/**
* Applies a value filter to this JSONObject.
*
* @param valueFilter the value filter to apply
* @since 2.0.3
*/
public void valueFilter(ValueFilter valueFilter) {
@ -1866,6 +1924,9 @@ public class JSONObject
}
/**
* Applies a name filter to this JSONObject.
*
* @param nameFilter the name filter to apply
* @since 2.0.3
*/
public void nameFilter(NameFilter nameFilter) {
@ -1881,6 +1942,10 @@ public class JSONObject
}
/**
* Evaluates a JSONPath expression against this JSONObject.
*
* @param path the JSONPath expression to evaluate
* @return the result of evaluating the JSONPath expression
* @see JSONPath#paths(Object)
*/
public Object eval(JSONPath path) {
@ -1888,9 +1953,11 @@ public class JSONObject
}
/**
* if value instance of Map or Collection, return size, other return 0
* Returns the size of the value associated with the given key if it is a Map or Collection.
* For other types, returns 0.
*
* @param key
* @param key the key whose associated value's size is to be returned
* @return the size of the value if it is a Map or Collection, otherwise 0
* @since 2.0.24
*/
public int getSize(String key) {

View File

@ -3,34 +3,96 @@ package com.alibaba.fastjson2;
import java.util.ArrayList;
import java.util.List;
/**
* JSONPObject is used to represent JSONP (JSON with Padding) data structure.
*
* <p>JSONP is a technique for safely requesting data from another domain.
* It wraps JSON data in a function call to bypass the same-origin policy
* that prevents direct access to resources from different domains.</p>
*
* <p>Example usage:
* <pre>{@code
* // Create a JSONP object
* JSONPObject jsonp = new JSONPObject("callback");
* jsonp.addParameter(new JSONObject().fluentPut("id", 1).fluentPut("name", "test"));
*
* // Serialize to JSONP string
* String jsonpString = jsonp.toString(); // "callback({\"id\":1,\"name\":\"test\"})"
*
* // Parse from JSONP string
* JSONPObject parsed = JSON.parseObject(jsonpString, JSONPObject.class);
* }</pre>
*
* @see <a href="https://en.wikipedia.org/wiki/JSONP">JSONP Wikipedia</a>
*/
public class JSONPObject {
/**
* The function name for JSONP callback
*/
private String function;
/**
* The parameters for JSONP function call
*/
private final List<Object> parameters = new ArrayList<>();
/**
* Default constructor for JSONPObject
*/
public JSONPObject() {
}
/**
* Constructor with function name
*
* @param function the JSONP callback function name
*/
public JSONPObject(String function) {
this.function = function;
}
/**
* Gets the function name of this JSONP object
*
* @return the function name
*/
public String getFunction() {
return function;
}
/**
* Sets the function name of this JSONP object
*
* @param function the function name to set
*/
public void setFunction(String function) {
this.function = function;
}
/**
* Gets the parameters list of this JSONP object
*
* @return the parameters list
*/
public List<Object> getParameters() {
return parameters;
}
/**
* Adds a parameter to this JSONP object
*
* @param parameter the parameter to add
*/
public void addParameter(Object parameter) {
this.parameters.add(parameter);
}
/**
* Serializes this JSONP object to JSONP string format
*
* @return the JSONP string representation
*/
@Override
public String toString() {
return JSON.toJSONString(this);
}

View File

@ -13,14 +13,83 @@ import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Represents a JSONPath expression for querying and manipulating JSON data.
*
* <p>JSONPath is a query language for JSON, similar to XPath for XML.
* It allows you to select and extract data from a JSON document.
*
* <p>Example usage:
* <pre>{@code
* // Parse JSON string and extract value using JSONPath
* String json = "{\"name\":\"John\", \"age\":30, \"city\":\"New York\"}";
* Object result = JSONPath.extract(json, "$.name");
* // result = "John"
*
* // Evaluate JSONPath on an object
* Map<String, Object> map = new HashMap<>();
* map.put("name", "John");
* map.put("age", 30);
* Object result = JSONPath.eval(map, "$.name");
* // result = "John"
*
* // Set value using JSONPath
* JSONPath.set(map, "$.name", "Jane");
* // map.get("name") = "Jane"
*
* // Remove value using JSONPath
* JSONPath.remove(map, "$.age");
* // map.size() = 1 (age field removed)
*
* // Check if path exists
* boolean exists = JSONPath.contains(map, "$.name");
* // exists = true
* }</pre>
*
* <p>Supported JSONPath expressions:
* <ul>
* <li><code>$</code> - Root object/element</li>
* <li><code>@</code> - Current object/element</li>
* <li><code>. or []</code> - Child operator</li>
* <li><code>..</code> - Recursive descent</li>
* <li><code>*</code> - Wildcard</li>
* <li><code>[]</code> - Array index or filter</li>
* <li><code>[start:end:step]</code> - Array slice</li>
* <li><code>?()</code> - Filter expression</li>
* </ul>
*
* @see <a href="https://github.com/json-path/JsonPath">JSONPath specification</a>
* @since 2.0.0
*/
public abstract class JSONPath {
static final JSONReader.Context PARSE_CONTEXT = JSONFactory.createReadContext();
/**
* Context for JSON reading operations
*/
JSONReader.Context readerContext;
/**
* Context for JSON writing operations
*/
JSONWriter.Context writerContext;
/**
* The JSONPath expression string
*/
final String path;
/**
* Feature flags for this JSONPath
*/
final long features;
/**
* Constructs a JSONPath with the specified path and features
*
* @param path the JSONPath expression
* @param features the features to apply
*/
protected JSONPath(String path, Feature... features) {
this.path = path;
long featuresValue = 0;
@ -30,47 +99,110 @@ public abstract class JSONPath {
this.features = featuresValue;
}
/**
* Constructs a JSONPath with the specified path and feature flags
*
* @param path the JSONPath expression
* @param features the feature flags to apply
*/
protected JSONPath(String path, long features) {
this.path = path;
this.features = features;
}
/**
* Gets the parent JSONPath of this path
*
* @return the parent JSONPath, or null if this is the root path
*/
public abstract JSONPath getParent();
/**
* Checks if this path ends with a filter
*
* @return true if this path ends with a filter, false otherwise
*/
public boolean endsWithFilter() {
return false;
}
/**
* Checks if this path represents a previous reference
*
* @return true if this path is a previous reference, false otherwise
*/
public boolean isPrevious() {
return false;
}
/**
* Returns the string representation of this JSONPath
*
* @return the JSONPath expression string
*/
@Override
public final String toString() {
return path;
}
/**
* Extracts a value from JSON string using the specified path
*
* @param json the JSON string to extract from
* @param path the JSONPath expression
* @return the extracted value, or null if not found
*/
public static Object extract(String json, String path) {
JSONReader jsonReader = JSONReader.of(json);
JSONPath jsonPath = JSONPath.of(path);
return jsonPath.extract(jsonReader);
}
/**
* Extracts a value from JSON string using the specified path and features
*
* @param json the JSON string to extract from
* @param path the JSONPath expression
* @param features the features to apply during extraction
* @return the extracted value, or null if not found
*/
public static Object extract(String json, String path, Feature... features) {
JSONReader jsonReader = JSONReader.of(json);
JSONPath jsonPath = JSONPath.of(path, features);
return jsonPath.extract(jsonReader);
}
/**
* Evaluates the JSONPath expression on a JSON string
*
* @param str the JSON string to evaluate
* @param path the JSONPath expression
* @return the evaluated result, or null if not found
*/
public static Object eval(String str, String path) {
return extract(str, path);
}
/**
* Evaluates the JSONPath expression on an object
*
* @param rootObject the root object to evaluate
* @param path the JSONPath expression
* @return the evaluated result, or null if not found
*/
public static Object eval(Object rootObject, String path) {
return JSONPath.of(path)
.eval(rootObject);
}
/**
* Sets a value in a JSON string using the specified path and returns the modified JSON
*
* @param json the JSON string to modify
* @param path the JSONPath expression
* @param value the value to set
* @return the modified JSON string
*/
public static String set(String json, String path, Object value) {
Object object = JSON.parse(json);
JSONPath.of(path)
@ -78,6 +210,13 @@ public abstract class JSONPath {
return JSON.toJSONString(object);
}
/**
* Checks if the specified path exists in the object
*
* @param rootObject the root object to check
* @param path the JSONPath expression
* @return true if the path exists, false otherwise
*/
public static boolean contains(Object rootObject, String path) {
if (rootObject == null) {
return false;
@ -87,6 +226,14 @@ public abstract class JSONPath {
return jsonPath.contains(rootObject);
}
/**
* Sets a value in an object using the specified path
*
* @param rootObject the root object to modify
* @param path the JSONPath expression
* @param value the value to set
* @return the modified root object
*/
public static Object set(Object rootObject, String path, Object value) {
JSONPath.of(path)
.set(rootObject, value);
@ -94,6 +241,14 @@ public abstract class JSONPath {
return rootObject;
}
/**
* Sets a callback function for the specified path in an object
*
* @param rootObject the root object to modify
* @param path the JSONPath expression
* @param callback the callback function to set
* @return the modified root object
*/
public static Object setCallback(Object rootObject, String path, Function callback) {
JSONPath.of(path)
.setCallback(rootObject, callback);
@ -101,6 +256,14 @@ public abstract class JSONPath {
return rootObject;
}
/**
* Sets a callback function for the specified path in an object
*
* @param rootObject the root object to modify
* @param path the JSONPath expression
* @param callback the callback function to set
* @return the modified root object
*/
public static Object setCallback(Object rootObject, String path, BiFunction callback) {
JSONPath.of(path)
.setCallback(rootObject, callback);
@ -108,6 +271,13 @@ public abstract class JSONPath {
return rootObject;
}
/**
* Removes a value from a JSON string using the specified path and returns the modified JSON
*
* @param json the JSON string to modify
* @param path the JSONPath expression
* @return the modified JSON string
*/
public static String remove(String json, String path) {
Object object = JSON.parse(json);
@ -117,11 +287,23 @@ public abstract class JSONPath {
return JSON.toJSONString(object);
}
/**
* Removes a value from an object using the specified path
*
* @param rootObject the root object to modify
* @param path the JSONPath expression
*/
public static void remove(Object rootObject, String path) {
JSONPath.of(path)
.remove(rootObject);
}
/**
* Gets all paths in the object
*
* @param javaObject the object to get paths from
* @return a map of paths to values
*/
public static Map<String, Object> paths(Object javaObject) {
Map<Object, String> values = new IdentityHashMap<>();
Map<String, Object> paths = new LinkedHashMap<>();
@ -130,6 +312,14 @@ public abstract class JSONPath {
return paths;
}
/**
* Recursively collects paths and values from the object
*
* @param values map to store object to path mappings
* @param paths map to store path to value mappings
* @param parent the parent path
* @param javaObject the object to process
*/
void paths(Map<Object, String> values, Map paths, String parent, Object javaObject) {
if (javaObject == null) {
return;
@ -243,8 +433,19 @@ public abstract class JSONPath {
}
}
/**
* Checks if this path is a reference
*
* @return true if this path is a reference, false otherwise
*/
public abstract boolean isRef();
/**
* Adds values to an array at the specified root object
*
* @param root the root object
* @param values the values to add
*/
public void arrayAdd(Object root, Object... values) {
Object result = eval(root);
if (result == null) {
@ -258,14 +459,37 @@ public abstract class JSONPath {
}
}
/**
* Checks if the path exists in the object
*
* @param object the object to check
* @return true if the path exists, false otherwise
*/
public abstract boolean contains(Object object);
/**
* Evaluates the path on the object
*
* @param object the object to evaluate
* @return the evaluation result
*/
public abstract Object eval(Object object);
/**
* Creates a new reading context
*
* @return a new JSONReader.Context
*/
protected JSONReader.Context createContext() {
return JSONFactory.createReadContext();
}
/**
* Extracts a value from a JSON string
*
* @param jsonStr the JSON string to extract from
* @return the extracted value, or null if input is null
*/
public Object extract(String jsonStr) {
if (jsonStr == null) {
return null;
@ -276,6 +500,12 @@ public abstract class JSONPath {
}
}
/**
* Extracts a value from a JSON byte array
*
* @param jsonBytes the JSON bytes to extract from
* @return the extracted value, or null if input is null
*/
public Object extract(byte[] jsonBytes) {
if (jsonBytes == null) {
return null;
@ -286,6 +516,15 @@ public abstract class JSONPath {
}
}
/**
* Extracts a value from a JSON byte array with specified offset, length and charset
*
* @param jsonBytes the JSON bytes to extract from
* @param off the offset in the byte array
* @param len the number of bytes to read
* @param charset the charset to use
* @return the extracted value, or null if input is null
*/
public Object extract(byte[] jsonBytes, int off, int len, Charset charset) {
if (jsonBytes == null) {
return null;
@ -296,10 +535,27 @@ public abstract class JSONPath {
}
}
/**
* Extracts a value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted value
*/
public abstract Object extract(JSONReader jsonReader);
/**
* Extracts a scalar value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted scalar value
*/
public abstract String extractScalar(JSONReader jsonReader);
/**
* Gets the reading context, creating it if necessary
*
* @return the JSONReader.Context
*/
public JSONReader.Context getReaderContext() {
if (readerContext == null) {
readerContext = JSONFactory.createReadContext();
@ -307,11 +563,22 @@ public abstract class JSONPath {
return readerContext;
}
/**
* Sets the reading context
*
* @param context the context to set
* @return this JSONPath instance
*/
public JSONPath setReaderContext(JSONReader.Context context) {
this.readerContext = context;
return this;
}
/**
* Gets the writing context, creating it if necessary
*
* @return the JSONWriter.Context
*/
public JSONWriter.Context getWriterContext() {
if (writerContext == null) {
writerContext = JSONFactory.createWriteContext();
@ -319,15 +586,40 @@ public abstract class JSONPath {
return writerContext;
}
/**
* Sets the writing context
*
* @param writerContext the context to set
* @return this JSONPath instance
*/
public JSONPath setWriterContext(JSONWriter.Context writerContext) {
this.writerContext = writerContext;
return this;
}
/**
* Sets a value in the object
*
* @param object the object to modify
* @param value the value to set
*/
public abstract void set(Object object, Object value);
/**
* Sets a value in the object with specified reader features
*
* @param object the object to modify
* @param value the value to set
* @param readerFeatures the reader features to apply
*/
public abstract void set(Object object, Object value, JSONReader.Feature... readerFeatures);
/**
* Sets a callback function for the object
*
* @param object the object to modify
* @param callback the callback function to set
*/
public void setCallback(Object object, Function callback) {
setCallback(
object,
@ -335,14 +627,44 @@ public abstract class JSONPath {
);
}
/**
* Sets a callback function for the object
*
* @param object the object to modify
* @param callback the callback function to set
*/
public abstract void setCallback(Object object, BiFunction callback);
/**
* Sets an integer value in the object
*
* @param object the object to modify
* @param value the integer value to set
*/
public abstract void setInt(Object object, int value);
/**
* Sets a long value in the object
*
* @param object the object to modify
* @param value the long value to set
*/
public abstract void setLong(Object object, long value);
/**
* Removes a value from the object
*
* @param object the object to modify
* @return true if removal was successful, false otherwise
*/
public abstract boolean remove(Object object);
/**
* Extracts a value using the provided JSONReader and passes it to the consumer
*
* @param jsonReader the JSONReader to use
* @param consumer the consumer to accept the extracted value
*/
public void extract(JSONReader jsonReader, ValueConsumer consumer) {
Object object = extract(jsonReader);
if (object == null) {
@ -378,6 +700,12 @@ public abstract class JSONPath {
throw new JSONException("TODO : " + object.getClass());
}
/**
* Extracts a scalar value using the provided JSONReader and passes it to the consumer
*
* @param jsonReader the JSONReader to use
* @param consumer the consumer to accept the extracted scalar value
*/
public void extractScalar(JSONReader jsonReader, ValueConsumer consumer) {
Object object = extractScalar(jsonReader);
if (object == null) {
@ -389,6 +717,12 @@ public abstract class JSONPath {
consumer.accept(str);
}
/**
* Extracts a Long value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted Long value, or null if the value was null
*/
public Long extractInt64(JSONReader jsonReader) {
long value = extractInt64Value(jsonReader);
if (jsonReader.wasNull) {
@ -397,6 +731,12 @@ public abstract class JSONPath {
return value;
}
/**
* Extracts a long value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted long value, or 0 if the value was null
*/
public long extractInt64Value(JSONReader jsonReader) {
Object object = extract(jsonReader);
if (object == null) {
@ -416,6 +756,12 @@ public abstract class JSONPath {
return (Long) converted;
}
/**
* Extracts an Integer value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted Integer value, or null if the value was null
*/
public Integer extractInt32(JSONReader jsonReader) {
int intValue = extractInt32Value(jsonReader);
if (jsonReader.wasNull) {
@ -424,6 +770,12 @@ public abstract class JSONPath {
return intValue;
}
/**
* Extracts an int value using the provided JSONReader
*
* @param jsonReader the JSONReader to use
* @return the extracted int value, or 0 if the value was null
*/
public int extractInt32Value(JSONReader jsonReader) {
Object object = extract(jsonReader);
if (object == null) {
@ -440,17 +792,37 @@ public abstract class JSONPath {
return (Integer) typeConvert.apply(object);
}
/**
* Compiles a JSONPath expression (deprecated, use {@link #of(String)} instead)
*
* @param path the JSONPath expression to compile
* @return the compiled JSONPath
* @deprecated use {@link #of(String)} instead
*/
@Deprecated
public static JSONPath compile(String path) {
return of(path);
}
/**
* Compiles a JSONPath expression for a specific object class
*
* @param strPath the JSONPath expression to compile
* @param objectClass the class of the object to use with this path
* @return the compiled JSONPath
*/
public static JSONPath compile(String strPath, Class objectClass) {
JSONPath path = of(strPath);
JSONFactory.JSONPathCompiler compiler = JSONFactory.getDefaultJSONPathCompiler();
return compiler.compile(objectClass, path);
}
/**
* Creates a single segment JSONPath from a segment
*
* @param segment the segment to create a path from
* @return the created JSONPath
*/
static JSONPathSingle of(JSONPathSegment segment) {
String prefix;
if (segment instanceof JSONPathSegment.MultiIndexSegment || segment instanceof JSONPathSegmentIndex) {
@ -466,6 +838,12 @@ public abstract class JSONPath {
return new JSONPathSingle(segment, path);
}
/**
* Creates a JSONPath from a path string
*
* @param path the JSONPath expression
* @return the created JSONPath
*/
public static JSONPath of(String path) {
if ("#-1".equals(path)) {
return PreviousPath.INSTANCE;
@ -475,21 +853,37 @@ public abstract class JSONPath {
.parse();
}
/**
* Creates a typed JSONPath from a path string and type
*
* @param path the JSONPath expression
* @param type the type of the result
* @return the created JSONPath
*/
public static JSONPath of(String path, Type type) {
JSONPath jsonPath = of(path);
return JSONPathTyped.of(jsonPath, type);
}
/**
* Creates a typed JSONPath from a path string, type and features
*
* @param path the JSONPath expression
* @param type the type of the result
* @param features the features to apply
* @return the created JSONPath
*/
public static JSONPath of(String path, Type type, Feature... features) {
JSONPath jsonPath = of(path, features);
return JSONPathTyped.of(jsonPath, type);
}
/**
* create multi-path jsonpath
* Creates a multi-path JSONPath
*
* @param paths jsonpath array
* @param types item types
* @param paths the JSONPath expressions
* @param types the types of the results
* @return the created JSONPath
* @since 2.0.20
*/
public static JSONPath of(String[] paths, Type[] types) {
@ -497,10 +891,12 @@ public abstract class JSONPath {
}
/**
* create multi-path jsonpath
* Creates a multi-path JSONPath
*
* @param paths jsonpath array
* @param types item types
* @param paths the JSONPath expressions
* @param types the types of the results
* @param features the reader features to apply
* @return the created JSONPath
* @since 2.0.20
*/
public static JSONPath of(String[] paths, Type[] types, JSONReader.Feature... features) {
@ -508,13 +904,15 @@ public abstract class JSONPath {
}
/**
* create multi-path jsonpath
* Creates a multi-path JSONPath
*
* @param paths jsonpath array
* @param types item types
* @param formats item format
* @param zoneId zonedId
* @param features parse use JSONReader.Features
* @param paths the JSONPath expressions
* @param types the types of the results
* @param formats the formats to apply
* @param pathFeatures the path features
* @param zoneId the zone ID
* @param features the reader features to apply
* @return the created JSONPath
* @since 2.0.20
*/
public static JSONPath of(
@ -851,6 +1249,13 @@ public abstract class JSONPath {
return new JSONPathTypedMulti(jsonPaths, types, formats, pathFeatures, zoneId, featuresValue);
}
/**
* Creates a JSONPath from a path string with specified features
*
* @param path the JSONPath expression
* @param features the features to apply
* @return the created JSONPath
*/
public static JSONPath of(String path, Feature... features) {
if ("#-1".equals(path)) {
return PreviousPath.INSTANCE;
@ -860,6 +1265,12 @@ public abstract class JSONPath {
.parse(features);
}
/**
* Parses an operator from the JSONReader
*
* @param jsonReader the JSONReader to parse from
* @return the parsed operator
*/
static JSONPathFilter.Operator parseOperator(JSONReader jsonReader) {
JSONPathFilter.Operator operator;
switch (jsonReader.ch) {
@ -1021,6 +1432,9 @@ public abstract class JSONPath {
return operator;
}
/**
* Represents a previous path reference
*/
static final class PreviousPath
extends JSONPath {
static final PreviousPath INSTANCE = new PreviousPath("#-1");
@ -1095,6 +1509,9 @@ public abstract class JSONPath {
}
}
/**
* Represents the root path ($)
*/
static final class RootPath
extends JSONPath {
static final RootPath INSTANCE = new RootPath();
@ -1168,6 +1585,9 @@ public abstract class JSONPath {
}
}
/**
* Represents the execution context for JSONPath operations
*/
static final class Context {
final JSONPath path;
final Context parent;
@ -1188,6 +1608,9 @@ public abstract class JSONPath {
}
}
/**
* Represents a sequence of values
*/
static class Sequence {
final List values;
@ -1196,9 +1619,23 @@ public abstract class JSONPath {
}
}
/**
* Features that can be applied to JSONPath operations
*/
public enum Feature {
/**
* Always return results as a list
*/
AlwaysReturnList(1),
/**
* Return null on error instead of throwing exceptions
*/
NullOnError(1 << 1),
/**
* Keep null values in results
*/
KeepNullValue(1 << 2);
public final long mask;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,18 @@
package com.alibaba.fastjson2;
/**
* Exception thrown when JSON schema validation fails.
*
* @author wenshao
* @since 2.0.59
*/
public class JSONSchemaValidException
extends JSONException {
/**
* Constructs a new JSONSchemaValidException with the specified detail message.
*
* @param message the detail message
*/
public JSONSchemaValidException(String message) {
super(message);
}

View File

@ -1,7 +1,18 @@
package com.alibaba.fastjson2;
/**
* Exception thrown when JSON validation fails.
*
* @author wenshao
* @since 2.0.59
*/
public class JSONValidException
extends JSONException {
/**
* Constructs a new JSONValidException with the specified detail message.
*
* @param message the detail message
*/
public JSONValidException(String message) {
super(message);
}

View File

@ -1,30 +1,104 @@
package com.alibaba.fastjson2;
/**
* A utility class for validating JSON strings or byte arrays to check if they
* represent valid JSON structures.
*
* <p>This class provides methods to validate JSON content and determine its type
* (Object, Array, or Value). It can handle both UTF-8 encoded byte arrays and
* String representations of JSON.</p>
*
* <p>Example usage:
* <pre>
* // Validate a JSON string
* JSONValidator validator = JSONValidator.from("{\"name\":\"John\", \"age\":30}");
* boolean isValid = validator.validate(); // returns true
* JSONValidator.Type type = validator.getType(); // returns Type.Object
*
* // Validate a JSON byte array
* byte[] jsonBytes = "[1, 2, 3]".getBytes(StandardCharsets.UTF_8);
* boolean isValidArray = JSONValidator.fromUtf8(jsonBytes).validate(); // returns true
* </pre>
* </p>
*
* @author wenshao[szujobs@hotmail.com]
* @since 2.0.59
*/
public class JSONValidator {
/**
* An enumeration representing the type of JSON structure.
*
* <p>JSON can be one of three types:
* <ul>
* <li>{@link #Object} - A JSON object, enclosed in curly braces {}</li>
* <li>{@link #Array} - A JSON array, enclosed in square brackets []</li>
* <li>{@link #Value} - A JSON value, which can be a string, number, boolean, or null</li>
* </ul>
* </p>
*/
public enum Type {
Object, Array, Value
/** Represents a JSON object structure (enclosed in curly braces {}) */
Object,
/** Represents a JSON array structure (enclosed in square brackets []) */
Array,
/** Represents a JSON value (string, number, boolean, or null) */
Value
}
private final JSONReader jsonReader;
private Boolean validateResult;
private Type type;
/**
* Constructs a new JSONValidator with the specified JSONReader.
*
* <p>This constructor is protected and intended for internal use.
* Use the static factory methods to create instances.</p>
*
* @param jsonReader the JSONReader to use for validation
*/
protected JSONValidator(JSONReader jsonReader) {
this.jsonReader = jsonReader;
}
/**
* Creates a new JSONValidator for the specified UTF-8 encoded byte array.
*
* @param jsonBytes the UTF-8 encoded byte array containing JSON content
* @return a new JSONValidator instance
*/
public static JSONValidator fromUtf8(byte[] jsonBytes) {
return new JSONValidator(JSONReader.of(jsonBytes));
}
/**
* Creates a new JSONValidator for the specified JSON string.
*
* @param jsonStr the string containing JSON content
* @return a new JSONValidator instance
*/
public static JSONValidator from(String jsonStr) {
return new JSONValidator(JSONReader.of(jsonStr));
}
/**
* Creates a new JSONValidator for the specified JSONReader.
*
* @param jsonReader the JSONReader containing JSON content
* @return a new JSONValidator instance
*/
public static JSONValidator from(JSONReader jsonReader) {
return new JSONValidator(jsonReader);
}
/**
* Validates the JSON content and returns true if it is valid JSON.
*
* <p>This method parses the JSON content to check its validity. The result is cached,
* so subsequent calls will return the same result without re-parsing.</p>
*
* @return true if the content is valid JSON, false otherwise
*/
public boolean validate() {
if (validateResult != null) {
return validateResult;
@ -51,6 +125,14 @@ public class JSONValidator {
return validateResult = jsonReader.isEnd();
}
/**
* Returns the type of the JSON content.
*
* <p>If the type has not yet been determined, this method will call {@link #validate()}
* to parse the content and determine its type.</p>
*
* @return the Type of the JSON content (Object, Array, or Value)
*/
public Type getType() {
if (type == null) {
validate();

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,22 @@ import com.alibaba.fastjson2.util.BeanUtils;
/**
* An enumeration that defines a few standard naming conventions for JSON field names.
*
* <p>This enum provides various naming strategies that can be used to transform Java field names
* to different JSON field naming conventions. For example, camelCase can be transformed to snake_case,
* kebab-case, or other formats.</p>
*
* <p>Example usage:
* <pre>
* // Using snake_case naming strategy
* PropertyNamingStrategy.SnakeCase.fieldName("userName"); // returns "user_name"
*
* // Using PascalCase naming strategy
* PropertyNamingStrategy.PascalCase.fieldName("userName"); // returns "UserName"
* </pre>
* </p>
*
* @author wenshao[szujobs@hotmail.com]
* @since 1.2.15
*/
public enum PropertyNamingStrategy {
@ -128,10 +144,30 @@ public enum PropertyNamingStrategy {
LowerCaseWithDots,
NeverUseThisValueExceptDefaultValue;
/**
* Transforms the given field name according to this naming strategy.
*
* @param name the original field name in Java (typically camelCase)
* @return the transformed field name according to the naming strategy
*/
public String fieldName(String name) {
return BeanUtils.fieldName(name, this.name());
}
/**
* Converts a snake_case string to camelCase.
*
* <p>For example:
* <ul>
* <li>"user_name" becomes "userName"</li>
* <li>"first_name" becomes "firstName"</li>
* <li>"url" remains "url" (no underscores)</li>
* </ul>
* </p>
*
* @param name the snake_case string to convert
* @return the camelCase string, or the original string if it doesn't contain underscores
*/
public static String snakeToCamel(String name) {
if (name == null || name.indexOf('_') == -1) {
return name;
@ -162,6 +198,21 @@ public enum PropertyNamingStrategy {
return new String(chars);
}
/**
* Returns the PropertyNamingStrategy enum constant with the specified name.
*
* <p>The string matching is case-insensitive for common aliases:
* <ul>
* <li>"Upper" or "upper" will return {@link #UpperCase}</li>
* <li>"Lower" or "lower" will return {@link #LowerCase}</li>
* <li>"Camel" or "camel" will return {@link #CamelCase}</li>
* </ul>
* Other strategy names are matched case-sensitively against the enum constant names.
* </p>
*
* @param strategy the name of the strategy to find, or null/empty to return null
* @return the PropertyNamingStrategy with the specified name, or null if not found
*/
public static PropertyNamingStrategy of(String strategy) {
if (strategy == null || strategy.isEmpty()) {
return null;

View File

@ -7,6 +7,16 @@ import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* Symbol table for fast name lookup.
*
* <p>This class provides a way to efficiently map names (strings) to ordinals and vice versa.
* It uses FNV-1a hash algorithm for fast hashing and maintains sorted hash codes for binary search.
*
* <p>SymbolTable is designed to be immutable after construction, making it thread-safe.
*
* @since 2.0.58
*/
public final class SymbolTable {
private final String[] names;
private final long hashCode64;
@ -16,16 +26,22 @@ public final class SymbolTable {
private final long[] hashCodesOrigin;
/**
* Create a symbol table
* Create a symbol table from class names.
*
* @param input input
* @param input classes whose names will be added to the symbol table
* @since 2.0.58
*/
public SymbolTable(Class<?>... input) {
this(classNames(input));
}
private static String[] classNames(Class... input) {
/**
* Extract class names from Class objects.
*
* @param input Class objects
* @return array of class names
*/
private static String[] classNames(Class<?>... input) {
String[] names = new String[input.length];
for (int i = 0; i < input.length; i++) {
names[i] = input[i].getName();
@ -33,6 +49,14 @@ public final class SymbolTable {
return names;
}
/**
* Create a symbol table from string names.
*
* <p>The names will be sorted and deduplicated. Each name is assigned a unique ordinal
* starting from 1. The ordinal 0 is reserved and means "not found".
*
* @param input names to be added to the symbol table
*/
public SymbolTable(String... input) {
Set<String> set = new TreeSet<>(Arrays.asList(input));
names = new String[set.size()];
@ -69,14 +93,33 @@ public final class SymbolTable {
this.hashCode64 = hashCode64;
}
/**
* Get the number of names in this symbol table.
*
* @return the number of names
*/
public int size() {
return names.length;
}
/**
* Get the 64-bit hash code of this symbol table.
*
* <p>The hash code is computed from all the names in the symbol table.
* It can be used to quickly compare if two symbol tables have the same content.
*
* @return the 64-bit hash code of this symbol table
*/
public long hashCode64() {
return hashCode64;
}
/**
* Get the name by its hash code.
*
* @param hashCode the FNV-1a 64-bit hash code of the name
* @return the name if found, {@code null} otherwise
*/
public String getNameByHashCode(long hashCode) {
int m = Arrays.binarySearch(hashCodes, hashCode);
if (m < 0) {
@ -87,6 +130,12 @@ public final class SymbolTable {
return names[index];
}
/**
* Get the ordinal of a name by its hash code.
*
* @param hashCode the FNV-1a 64-bit hash code of the name
* @return the ordinal (1-based) if found, -1 otherwise
*/
public int getOrdinalByHashCode(long hashCode) {
int m = Arrays.binarySearch(hashCodes, hashCode);
if (m < 0) {
@ -96,6 +145,12 @@ public final class SymbolTable {
return this.mapping[m] + 1;
}
/**
* Get the ordinal of a name.
*
* @param name the name to look up
* @return the ordinal (1-based) if found, -1 otherwise
*/
public int getOrdinal(String name) {
int m = Arrays.binarySearch(hashCodes, Fnv.hashCode64(name));
if (m < 0) {
@ -105,10 +160,24 @@ public final class SymbolTable {
return this.mapping[m] + 1;
}
/**
* Get the name by its ordinal.
*
* @param ordinal the ordinal (1-based) of the name
* @return the name at the specified ordinal
* @throws ArrayIndexOutOfBoundsException if the ordinal is invalid
*/
public String getName(int ordinal) {
return names[ordinal - 1];
}
/**
* Get the hash code of a name by its ordinal.
*
* @param ordinal the ordinal (1-based) of the name
* @return the FNV-1a 64-bit hash code of the name at the specified ordinal
* @throws ArrayIndexOutOfBoundsException if the ordinal is invalid
*/
public long getHashCode(int ordinal) {
return hashCodesOrigin[ordinal - 1];
}

View File

@ -14,32 +14,41 @@ import java.util.List;
import java.util.Map;
/**
* Represents a generic type {@code T}. Java doesn't yet provide a way to
* represent generic types, so this class does. Forces clients to create a
* subclass of this class which enables retrieval the type information even at runtime.
* <p>
* This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class<T>} or {@code List<? extends CharSequence>}.
* <p>
* For example, to create a type literal for {@code List<String>}, you can
* create an empty anonymous inner class:
* Represents a generic type {@code T}.
*
* <p>Java doesn't yet provide a way to represent generic types, so this class does.
* Forces clients to create a subclass of this class which enables retrieval the type
* information even at runtime.</p>
*
* <p>This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class<T>} or {@code List<? extends CharSequence>}.</p>
*
* <p>For example, to create a type literal for {@code List<String>}, you can
* create an empty anonymous inner class:</p>
* <pre>{@code
* TypeReference<List<String>> typeReference = new TypeReference<List<String>>(){};
* }</pre>
* For example, use it quickly
* <pre>{@code String text = "{\"id\":1,\"name\":\"kraity\"}";
*
* <p>For example, use it quickly:</p>
* <pre>{@code
* String text = "{\"id\":1,\"name\":\"kraity\"}";
* User user = new TypeReference<User>(){}.parseObject(text);
* }</pre>
*
* @param <T> the type refered to
* @author wenshao[szujobs@hotmail.com]
* @since 2.0.2
*/
public abstract class TypeReference<T> {
protected final Type type;
protected final Class<? super T> rawType;
/**
* Constructs a new type literal. Derives represented class from type parameter.
* <p>
* Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy, so we can reconstitute it at runtime despite erasure.
*
* <p>Clients create an empty anonymous subclass. Doing so embeds the type
* parameter in the anonymous class's type hierarchy, so we can reconstitute it at runtime despite erasure.</p>
*/
@SuppressWarnings("unchecked")
public TypeReference() {
@ -49,6 +58,8 @@ public abstract class TypeReference<T> {
}
/**
* Constructs a new type literal with the specified type.
*
* @param type specify the {@link Type} to be converted
* @throws NullPointerException If the {@link Type} is null
*/
@ -63,7 +74,9 @@ public abstract class TypeReference<T> {
}
/**
* E.g.
* Constructs a new type literal with the specified actual type arguments.
*
* <p>For example:</p>
* <pre>{@code
* Class<T> klass = ...;
* TypeReference<Response<T>> ref = new TypeReference<Response<T>>(new Type[]{klass}){};
@ -93,14 +106,18 @@ public abstract class TypeReference<T> {
}
/**
* Get the {@link Type}
* Gets the type literal.
*
* @return the {@link Type}
*/
public final Type getType() {
return type;
}
/**
* Get the raw {@link Class}
* Gets the raw type.
*
* @return the raw {@link Class}
*/
public final Class<? super T> getRawType() {
return rawType;
@ -223,7 +240,10 @@ public abstract class TypeReference<T> {
}
/**
* Gets a type reference for the specified type.
*
* @param type specify the {@link Type} to be converted
* @return the type reference
*/
public static TypeReference<?> get(Type type) {
return new TypeReference<Object>(type) {
@ -232,10 +252,13 @@ public abstract class TypeReference<T> {
}
/**
* Canonicalize the type.
*
* @param thisClass this class
* @param type the parameterizedType
* @param actualTypeArguments an array of Type objects representing the actual type arguments to this type
* @param actualIndex the actual index
* @return the canonicalized type
* @since 2.0.3
*/
private static Type canonicalize(Class<?> thisClass,
@ -312,10 +335,23 @@ public abstract class TypeReference<T> {
);
}
/**
* Creates a multi-type from the specified types.
*
* @param types the types
* @return the multi-type
*/
public static Type of(Type... types) {
return new MultiType(types);
}
/**
* Creates a collection type with the specified collection class and element class.
*
* @param collectionClass the collection class
* @param elementClass the element class
* @return the parameterized type
*/
public static Type collectionType(
Class<? extends Collection> collectionClass,
Class<?> elementClass
@ -323,10 +359,24 @@ public abstract class TypeReference<T> {
return new ParameterizedTypeImpl(collectionClass, elementClass);
}
/**
* Creates an array type with the specified element type.
*
* @param elementType the element type
* @return the generic array type
*/
public static Type arrayType(Class<?> elementType) {
return new BeanUtils.GenericArrayTypeImpl(elementType);
}
/**
* Creates a map type with the specified map class, key class and value class.
*
* @param mapClass the map class
* @param keyClass the key class
* @param valueClass the value class
* @return the parameterized type
*/
public static Type mapType(
Class<? extends Map> mapClass,
Class<?> keyClass,
@ -335,6 +385,13 @@ public abstract class TypeReference<T> {
return new ParameterizedTypeImpl(mapClass, keyClass, valueClass);
}
/**
* Creates a map type with the specified key class and value type.
*
* @param keyClass the key class
* @param valueType the value type
* @return the parameterized type
*/
public static Type mapType(
Class<?> keyClass,
Type valueType
@ -342,10 +399,24 @@ public abstract class TypeReference<T> {
return new ParameterizedTypeImpl(Map.class, keyClass, valueType);
}
/**
* Creates a parameterized type with the specified parametrized class and parameter classes.
*
* @param parametrized the parametrized class
* @param parameterClasses the parameter classes
* @return the parameterized type
*/
public static Type parametricType(Class<?> parametrized, Class<?>... parameterClasses) {
return new ParameterizedTypeImpl(parametrized, parameterClasses);
}
/**
* Creates a parameterized type with the specified parametrized class and parameter types.
*
* @param parametrized the parametrized class
* @param parameterTypes the parameter types
* @return the parameterized type
*/
public static Type parametricType(Class<?> parametrized, Type... parameterTypes) {
return new ParameterizedTypeImpl(parametrized, parameterTypes);
}

View File

@ -8,6 +8,39 @@ 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");
@ -53,13 +86,39 @@ public interface ObjectReader<T> {
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++) {
@ -99,6 +158,14 @@ public interface ObjectReader<T> {
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();
@ -120,39 +187,82 @@ public interface ObjectReader<T> {
}
/**
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
* 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();
}
/**
* Features enabled by ObjectReader
* 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) {
@ -162,6 +272,15 @@ public interface ObjectReader<T> {
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) {
@ -171,6 +290,12 @@ public interface ObjectReader<T> {
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);
@ -188,6 +313,14 @@ public interface ObjectReader<T> {
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) {
@ -197,21 +330,46 @@ public interface ObjectReader<T> {
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);
}
/**
* @return {@link T}
* @throws JSONException If a suitable ObjectReader is not found
* 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() &&
@ -280,23 +438,39 @@ public interface ObjectReader<T> {
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
* 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();
}
/**
* @return {@link T}
* @throws UnsupportedOperationException If the method is not overloaded or otherwise
* 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();
}
/**
* @return {@link T}
* 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))) {
@ -305,19 +479,35 @@ public interface ObjectReader<T> {
}
/**
* @return {@link T}
* 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);
}
/**
* @return {@link T}
* @throws JSONException If a suitable ObjectReader is not found
* 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);
}

View File

@ -77,6 +77,35 @@ import java.util.stream.Collectors;
import static com.alibaba.fastjson2.util.BeanUtils.*;
import static com.alibaba.fastjson2.util.TypeUtils.*;
/**
* ObjectReaderCreator is responsible for creating ObjectReader instances for
* deserializing JSON data into Java objects. It provides factory methods for
* creating ObjectReaders for various types of objects and fields.
*
* <p>This class supports various features including:
* <ul>
* <li>Creation of ObjectReaders for different object types</li>
* <li>Creation of FieldReaders for different field types</li>
* <li>Lambda expression support for setter methods</li>
* <li>Custom field reader creation with various configurations</li>
* <li>JIT compilation support for improved performance</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get default creator
* ObjectReaderCreator creator = JSONFactory.getDefaultObjectReaderCreator();
*
* // Create ObjectReader for a class
* ObjectReader&lt;User&gt; reader = creator.createObjectReader(User.class);
*
* // Create FieldReader for a field
* Field field = User.class.getDeclaredField("name");
* FieldReader&lt;User&gt; fieldReader = creator.createFieldReader("name", String.class, field);
* </pre>
*
* @since 2.0.0
*/
public class ObjectReaderCreator {
public static final boolean JIT = !JDKUtils.ANDROID && !JDKUtils.GRAAL;
public static final ObjectReaderCreator INSTANCE = new ObjectReaderCreator();
@ -271,11 +300,27 @@ public class ObjectReaderCreator {
return fieldReaders.toArray(new FieldReader[0]);
}
/**
* Creates a Function that can instantiate objects using the specified factory method and parameter names.
*
* @param <T> the type of objects that the Function can create
* @param factoryMethod the factory method to use for creating instances
* @param paramNames the parameter names to use for the factory method
* @return a Function that can create new instances of the specified type using the factory method
*/
public <T> Function<Map<Long, Object>, T> createFactoryFunction(Method factoryMethod, String... paramNames) {
factoryMethod.setAccessible(true);
return new FactoryFunction(factoryMethod, paramNames);
}
/**
* Creates a Function that can instantiate objects using the specified constructor and parameter names.
*
* @param <T> the type of objects that the Function can create
* @param constructor the constructor to use for creating instances
* @param paramNames the parameter names to use for the constructor
* @return a Function that can create new instances of the specified type
*/
public <T> Function<Map<Long, Object>, T> createFunction(Constructor constructor, String... paramNames) {
constructor.setAccessible(true);
return new ConstructorFunction(
@ -288,6 +333,15 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a Function that can instantiate objects using the specified constructor, marker constructor, and parameter names.
*
* @param <T> the type of objects that the Function can create
* @param constructor the constructor to use for creating instances
* @param markerConstructor the marker constructor to use
* @param paramNames the parameter names to use for the constructor
* @return a Function that can create new instances of the specified type
*/
public <T> Function<Map<Long, Object>, T> createFunction(
Constructor constructor,
Constructor markerConstructor,
@ -308,6 +362,14 @@ public class ObjectReaderCreator {
);
}
/**
* Creates an ObjectReader for the specified object type with the given field readers.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
FieldReader... fieldReaders
@ -323,6 +385,15 @@ public class ObjectReaderCreator {
);
}
/**
* Creates an ObjectReader for the specified object type with a default creator and field readers.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param defaultCreator the supplier function to create new instances of the object
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
Supplier<T> defaultCreator,
@ -331,6 +402,15 @@ public class ObjectReaderCreator {
return createObjectReader(objectClass, null, 0, null, defaultCreator, null, fieldReaders);
}
/**
* Creates an ObjectReader for the specified object type with see-also support.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectType the class of objects to deserialize
* @param seeAlso the see-also classes
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReaderSeeAlso(
Class<T> objectType,
Class[] seeAlso,
@ -348,6 +428,17 @@ public class ObjectReaderCreator {
);
}
/**
* Creates an ObjectReader for the specified object type with see-also support and custom type key.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param typeKey the type key to use
* @param seeAlso the see-also classes
* @param seeAlsoNames the see-also class names
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReaderSeeAlso(
Class<T> objectClass,
String typeKey,
@ -368,6 +459,16 @@ public class ObjectReaderCreator {
}
/**
* Creates an ObjectReader for the specified object type with see-also support, custom type key, and default class.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param typeKey the type key to use
* @param seeAlso the see-also classes
* @param seeAlsoNames the see-also class names
* @param seeAlsoDefault the default see-also class
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
* @since 2.0.24
*/
public <T> ObjectReader<T> createObjectReaderSeeAlso(
@ -390,6 +491,18 @@ public class ObjectReaderCreator {
);
}
/**
* Creates an ObjectReader for the specified object type with see-also support, custom creator, and type key.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectType the class of objects to deserialize
* @param defaultCreator the supplier function to create new instances of the object
* @param typeKey the type key to use
* @param seeAlso the see-also classes
* @param seeAlsoNames the see-also class names
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReaderSeeAlso(
Class<T> objectType,
Supplier<T> defaultCreator,
@ -783,6 +896,17 @@ public class ObjectReaderCreator {
);
}
/**
* Creates an ObjectReader for the specified object type with comprehensive configuration.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param features the features to use for deserialization
* @param defaultCreator the supplier function to create new instances of the object
* @param buildFunction the build function to use
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
long features,
@ -793,6 +917,19 @@ public class ObjectReaderCreator {
return createObjectReader(objectClass, null, features, null, defaultCreator, buildFunction, fieldReaders);
}
/**
* Creates an ObjectReader for the specified object type with type key, features, schema, and comprehensive configuration.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param typeKey the type key to use
* @param features the features to use for deserialization
* @param schema the JSON schema to use
* @param defaultCreator the supplier function to create new instances of the object
* @param buildFunction the build function to use
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
String typeKey,
@ -813,6 +950,20 @@ public class ObjectReaderCreator {
fieldReaders);
}
/**
* Creates an ObjectReader for the specified object type with comprehensive configuration including root name.
*
* @param <T> the type of objects that the ObjectReader can deserialize
* @param objectClass the class of objects to deserialize
* @param typeKey the type key to use
* @param rootName the root name to use
* @param features the features to use for deserialization
* @param schema the JSON schema to use
* @param defaultCreator the supplier function to create new instances of the object
* @param buildFunction the build function to use
* @param fieldReaders the field readers to use for deserialization
* @return an ObjectReader instance for the specified type
*/
public <T> ObjectReader<T> createObjectReader(
Class<T> objectClass,
String typeKey,
@ -1785,6 +1936,13 @@ public class ObjectReaderCreator {
return toFieldReaderArray(fieldReaders);
}
/**
* Creates a Supplier function for the specified object class that can create new instances.
*
* @param <T> the type of objects that the Supplier can create
* @param objectClass the class of objects to create
* @return a Supplier function that can create new instances of the specified class, or null if creation is not possible
*/
public <T> Supplier<T> createSupplier(Class<T> objectClass) {
if (objectClass.isInterface()) {
return null;
@ -1805,9 +1963,28 @@ public class ObjectReaderCreator {
throw new JSONException("get constructor error, class " + objectClass.getName(), e);
}
return createSupplier(constructor);
}
/**
* Creates a Supplier function for the specified constructor that can create new instances.
*
* @param <T> the type of objects that the Supplier can create
* @param constructor the constructor to use for creating instances
* @return a Supplier function that can create new instances using the specified constructor
*/
public <T> Supplier<T> createSupplier(Constructor<T> constructor) {
return createSupplier(constructor, true);
}
/**
* Creates a Supplier function for the specified constructor with JIT compilation option.
*
* @param <T> the type of objects that the Supplier can create
* @param constructor the constructor to use for creating instances
* @param jit whether to use JIT compilation for improved performance
* @return a Supplier function that can create new instances using the specified constructor
*/
public <T> Supplier<T> createSupplier(Constructor constructor, boolean jit) {
jit &= JIT;
@ -1975,6 +2152,14 @@ public class ObjectReaderCreator {
return null;
}
/**
* Creates a Function that can build objects using the specified builder method.
*
* @param <T> the type of objects that the Function can build
* @param <R> the return type of the builder method
* @param builderMethod the builder method to use for building objects
* @return a Function that can build objects using the specified builder method
*/
public <T, R> Function<T, R> createBuildFunction(Method builderMethod) {
try {
return createBuildFunctionLambda(builderMethod);
@ -1994,6 +2179,15 @@ public class ObjectReaderCreator {
};
}
/**
* Creates a Function that can build objects using the specified builder method with lambda optimization.
*
* @param <T> the type of objects that the Function can build
* @param <R> the return type of the builder method
* @param builderMethod the builder method to use for building objects
* @return a Function that can build objects using the specified builder method
* @throws Throwable if an error occurs during lambda creation
*/
<T, R> Function<T, R> createBuildFunctionLambda(Method builderMethod) {
MethodHandles.Lookup lookup = JDKUtils.trustedLookup(builderMethod.getDeclaringClass());
try {
@ -2021,6 +2215,17 @@ public class ObjectReaderCreator {
}
}
/**
* Creates a FieldReader for the specified field with default configuration.
*
* @param <T> the type of objects that contain the field
* @param objectType the class containing the field
* @param fieldName the name of the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @return a FieldReader instance for the specified field
*/
public <T> FieldReader createFieldReader(
Class<T> objectType,
String fieldName,
@ -2045,6 +2250,18 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a FieldReader for the specified field with format configuration.
*
* @param <T> the type of objects that contain the field
* @param objectType the class containing the field
* @param fieldName the name of the field
* @param format the date format to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @return a FieldReader instance for the specified field
*/
public <T> FieldReader createFieldReader(
Class<T> objectType,
String fieldName,
@ -2056,8 +2273,20 @@ public class ObjectReaderCreator {
return createFieldReaderMethod(objectType, fieldName, format, fieldType, fieldClass, method);
}
/**
* Creates a FieldReader for the specified method with default configuration.
*
* @param <T> the type of objects that contain the field
* @param objectType the class containing the field
* @param fieldName the name of the field
* @param format the date format to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @return a FieldReader instance for the specified field
*/
public <T> FieldReader createFieldReaderMethod(
Class<T> objectClass,
Class<T> objectType,
String fieldName,
String format,
Type fieldType,
@ -2065,8 +2294,8 @@ public class ObjectReaderCreator {
Method method
) {
return createFieldReaderMethod(
objectClass,
objectClass,
objectType,
objectType,
fieldName,
0,
0L,
@ -2081,6 +2310,24 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a FieldReader for the specified parameter with default configuration.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param paramName the name of the parameter
* @param declaringClass the declaring class of the parameter
* @param parameter the parameter to create a reader for
* @param schema the JSON schema to use
* @return a FieldReader instance for the specified parameter
*/
public <T> FieldReader createFieldReaderParam(
Class<T> objectClass,
Type objectType,
@ -2112,6 +2359,25 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a FieldReader for the specified parameter with initialization reader.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param paramName the name of the parameter
* @param declaringClass the declaring class of the parameter
* @param parameter the parameter to create a reader for
* @param schema the JSON schema to use
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified parameter
*/
public <T> FieldReader createFieldReaderParam(
Class<T> objectClass,
Type objectType,
@ -2146,6 +2412,69 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a FieldReader for the specified parameter with comprehensive configuration.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param paramName the name of the parameter
* @param declaringClass the declaring class of the parameter
* @param parameter the parameter to create a reader for
* @param schema the JSON schema to use
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified parameter
*/
/**
* Creates a FieldReader for the specified parameter with comprehensive configuration including locale, default value, and initialization reader.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param paramName the name of the parameter
* @param declaringClass the declaring class of the parameter
* @param parameter the parameter to create a reader for
* @param schema the JSON schema to use for the field
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified parameter
*/
/**
* Creates a FieldReader for the specified parameter with comprehensive configuration.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param paramName the name of the parameter
* @param declaringClass the declaring class of the parameter
* @param parameter the parameter to create a reader for
* @param schema the JSON schema to use for the field
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified parameter
*/
public <T> FieldReader createFieldReaderParam(
Class<T> objectClass,
Type objectType,
@ -2257,6 +2586,25 @@ public class ObjectReaderCreator {
schema);
}
/**
* Creates a FieldReader for the specified method with comprehensive configuration.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param schema the schema to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified method
*/
public <T> FieldReader createFieldReaderMethod(
Class<T> objectClass,
Type objectType,
@ -2291,6 +2639,27 @@ public class ObjectReaderCreator {
);
}
/**
* Creates a FieldReader for the specified method with comprehensive configuration including array-to-map options.
*
* @param <T> the type of objects that contain the field
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for the field
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param schema the schema to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @param initReader the initialization reader to use
* @param keyName the key name for array-to-map conversion
* @param arrayToMapDuplicateHandler the duplicate handler for array-to-map conversion
* @return a FieldReader instance for the specified method
*/
public <T> FieldReader createFieldReaderMethod(
Class<T> objectClass,
Type objectType,
@ -2680,6 +3049,15 @@ public class ObjectReaderCreator {
return createFieldReader(fieldName, null, field.getGenericType(), field);
}
/**
* Creates a FieldReader for the specified method with minimal configuration.
*
* @param fieldName the name of the field
* @param method the method to create a reader for
* @param <T> the type of objects that the FieldReader can deserialize
* @return a FieldReader instance for the specified method
* @throws JSONException if the method has an illegal number of parameters
*/
public <T> FieldReader createFieldReader(
String fieldName,
Method method
@ -3428,6 +3806,25 @@ public class ObjectReaderCreator {
return initReader;
}
/**
* Creates a FieldReader using lambda expressions for improved performance.
*
* @param <T> the type of objects that the FieldReader can deserialize
* @param objectClass the class containing the field
* @param objectType the type of the object
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use for deserialization
* @param format the date format to use for the field
* @param locale the locale to use for the field
* @param defaultValue the default value for the field
* @param schema the JSON schema to use for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the method to use for reading the field
* @param initReader the initialization reader to use
* @return a FieldReader instance for the specified field
*/
protected <T> FieldReader createFieldReaderLambda(
Class<T> objectClass,
Type objectType,

View File

@ -30,6 +30,37 @@ import static com.alibaba.fastjson2.util.Fnv.MAGIC_HASH_CODE;
import static com.alibaba.fastjson2.util.Fnv.MAGIC_PRIME;
import static com.alibaba.fastjson2.util.TypeUtils.loadClass;
/**
* ObjectReaderProvider is responsible for providing and managing ObjectReader instances
* for deserializing JSON data into Java objects. It handles object creation, caching,
* type conversion, and auto-type support.
*
* <p>This provider supports various features including:
* <ul>
* <li>Object reader caching for performance optimization</li>
* <li>Auto-type support with security controls</li>
* <li>Type conversion between different Java types</li>
* <li>Mixin support for modifying serialization behavior</li>
* <li>Module-based extensibility</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get default provider
* ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
*
* // Get object reader for a specific type
* ObjectReader&lt;User&gt; reader = provider.getObjectReader(User.class);
*
* // Parse JSON string using the reader
* User user = reader.readObject(JSONReader.of(jsonString));
*
* // Register custom type converter
* provider.registerTypeConvert(String.class, Integer.class, Integer::valueOf);
* </pre>
*
* @since 2.0.0
*/
public class ObjectReaderProvider
implements ObjectCodecProvider {
static final ClassLoader FASTJSON2_CLASS_LOADER = JSON.class.getClassLoader();
@ -193,6 +224,13 @@ public class ObjectReaderProvider
hashCache.put(Fnv.hashCode64(TypeUtils.getTypeName(HashMap.class)), ObjectReaderImplMap.INSTANCE);
}
/**
* Registers an ObjectReader for the specified hash code if it is not already registered.
* This method handles both thread-local and global caching.
*
* @param hashCode the hash code for which to register the ObjectReader
* @param objectReader the ObjectReader to register
*/
public void registerIfAbsent(long hashCode, ObjectReader objectReader) {
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
if (tcl != null && tcl != JSON.class.getClassLoader()) {
@ -209,6 +247,12 @@ public class ObjectReaderProvider
hashCache.putIfAbsent(hashCode, objectReader);
}
/**
* Adds a type name to the auto-type accept list. Types in this list are allowed
* for auto-type deserialization.
*
* @param name the type name to add to the accept list
*/
public void addAutoTypeAccept(String name) {
if (name != null && name.length() != 0) {
long hash = Fnv.hashCode64(name);
@ -222,26 +266,49 @@ public class ObjectReaderProvider
}
}
@Deprecated
public void addAutoTypeDeny(String name) {
}
/**
* Gets the auto-type handler that is invoked when a type is auto-resolved.
*
* @return the auto-type handler, or null if none is set
*/
public Consumer<Class> getAutoTypeHandler() {
return autoTypeHandler;
}
/**
* Sets the auto-type handler that will be invoked when a type is auto-resolved.
*
* @param autoTypeHandler the auto-type handler to set
*/
public void setAutoTypeHandler(Consumer<Class> autoTypeHandler) {
this.autoTypeHandler = autoTypeHandler;
}
/**
* Gets the mixin source class for the specified target class.
*
* @param target the target class
* @return the mixin source class, or null if no mixin is registered for the target
*/
public Class getMixIn(Class target) {
return mixInCache.get(target);
}
/**
* Clears all mixin mappings.
*/
public void cleanupMixIn() {
mixInCache.clear();
}
/**
* Registers a mixin mapping between a target class and a mixin source class.
* Mixin allows modifying the serialization/deserialization behavior of a class
* by applying annotations from another class.
*
* @param target the target class to which the mixin will be applied
* @param mixinSource the source class from which annotations will be copied, or null to remove the mixin
*/
public void mixIn(Class target, Class mixinSource) {
if (mixinSource == null) {
mixInCache.remove(target);
@ -252,10 +319,23 @@ public class ObjectReaderProvider
cacheFieldBased.remove(target);
}
/**
* Registers a subtype class for see-also support. This allows the provider to
* recognize and handle subtypes of the specified superclass.
*
* @param subTypeClass the subtype class to register
*/
public void registerSeeAlsoSubType(Class subTypeClass) {
registerSeeAlsoSubType(subTypeClass, null);
}
/**
* Registers a subtype class with a specific name for see-also support.
*
* @param subTypeClass the subtype class to register
* @param subTypeClassName the name of the subtype class, or null to use the class's simple name
* @throws JSONException if the superclass is null
*/
public void registerSeeAlsoSubType(Class subTypeClass, String subTypeClassName) {
Class superClass = subTypeClass.getSuperclass();
if (superClass == null) {
@ -276,6 +356,15 @@ public class ObjectReaderProvider
}
}
/**
* Registers an ObjectReader for the specified type. If an ObjectReader is already
* registered for the type, it will be replaced.
*
* @param type the type for which to register the ObjectReader
* @param objectReader the ObjectReader to register, or null to unregister
* @param fieldBased whether the ObjectReader is field-based
* @return the previous ObjectReader for the type, or null if there was no previous ObjectReader
*/
public ObjectReader register(Type type, ObjectReader objectReader, boolean fieldBased) {
ConcurrentMap<Type, ObjectReader> cache = fieldBased ? this.cacheFieldBased : this.cache;
if (objectReader == null) {
@ -285,37 +374,98 @@ public class ObjectReaderProvider
return cache.put(type, objectReader);
}
/**
* Registers an ObjectReader for the specified type using method-based reading.
* If an ObjectReader is already registered for the type, it will be replaced.
*
* @param type the type for which to register the ObjectReader
* @param objectReader the ObjectReader to register, or null to unregister
* @return the previous ObjectReader for the type, or null if there was no previous ObjectReader
*/
public ObjectReader register(Type type, ObjectReader objectReader) {
return register(type, objectReader, false);
}
/**
* Registers an ObjectReader for the specified type using method-based reading
* if it is not already registered.
*
* @param type the type for which to register the ObjectReader
* @param objectReader the ObjectReader to register
* @return the previous ObjectReader for the type, or null if there was no previous ObjectReader
*/
public ObjectReader registerIfAbsent(Type type, ObjectReader objectReader) {
return registerIfAbsent(type, objectReader, false);
}
/**
* Registers an ObjectReader for the specified type if it is not already registered.
*
* @param type the type for which to register the ObjectReader
* @param objectReader the ObjectReader to register
* @param fieldBased whether the ObjectReader is field-based
* @return the previous ObjectReader for the type, or null if there was no previous ObjectReader
*/
public ObjectReader registerIfAbsent(Type type, ObjectReader objectReader, boolean fieldBased) {
ConcurrentMap<Type, ObjectReader> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.putIfAbsent(type, objectReader);
}
/**
* Unregisters the ObjectReader for the specified type using method-based reading.
*
* @param type the type for which to unregister the ObjectReader
* @return the unregistered ObjectReader, or null if there was no ObjectReader for the type
*/
public ObjectReader unregisterObjectReader(Type type) {
return unregisterObjectReader(type, false);
}
/**
* Unregisters the ObjectReader for the specified type.
*
* @param type the type for which to unregister the ObjectReader
* @param fieldBased whether the ObjectReader is field-based
* @return the unregistered ObjectReader, or null if there was no ObjectReader for the type
*/
public ObjectReader unregisterObjectReader(Type type, boolean fieldBased) {
ConcurrentMap<Type, ObjectReader> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.remove(type);
}
/**
* Unregisters the specified ObjectReader for the given type using method-based reading,
* but only if the currently registered reader matches the specified reader.
*
* @param type the type for which to unregister the ObjectReader
* @param reader the ObjectReader to unregister
* @return true if the ObjectReader was unregistered, false otherwise
*/
public boolean unregisterObjectReader(Type type, ObjectReader reader) {
return unregisterObjectReader(type, reader, false);
}
/**
* Unregisters the specified ObjectReader for the given type, but only if the currently
* registered reader matches the specified reader.
*
* @param type the type for which to unregister the ObjectReader
* @param reader the ObjectReader to unregister
* @param fieldBased whether the ObjectReader is field-based
* @return true if the ObjectReader was unregistered, false otherwise
*/
public boolean unregisterObjectReader(Type type, ObjectReader reader, boolean fieldBased) {
ConcurrentMap<Type, ObjectReader> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.remove(type, reader);
}
/**
* Registers an ObjectReaderModule. If the module is already registered, this method
* does nothing and returns false.
*
* @param module the module to register
* @return true if the module was registered, false if it was already registered
*/
public boolean register(ObjectReaderModule module) {
for (int i = modules.size() - 1; i >= 0; i--) {
if (modules.get(i) == module) {
@ -329,10 +479,21 @@ public class ObjectReaderProvider
return true;
}
/**
* Unregisters an ObjectReaderModule.
*
* @param module the module to unregister
* @return true if the module was unregistered, false if it was not registered
*/
public boolean unregister(ObjectReaderModule module) {
return modules.remove(module);
}
/**
* Cleans up cached ObjectReaders and mixin mappings associated with the specified class.
*
* @param objectClass the class for which to clean up cached ObjectReaders
*/
public void cleanup(Class objectClass) {
mixInCache.remove(objectClass);
cache.remove(objectClass);
@ -350,6 +511,8 @@ public class ObjectReaderProvider
}
/**
* Clears all cached ObjectReaders and mixin mappings.
*
* @since 2.0.53
*/
public void clear() {
@ -410,6 +573,13 @@ public class ObjectReaderProvider
return false;
}
/**
* Cleans up cached ObjectReaders associated with the specified ClassLoader.
* This method removes all cached readers that are related to classes loaded
* by the given ClassLoader.
*
* @param classLoader the ClassLoader for which to clean up cached ObjectReaders
*/
public void cleanup(ClassLoader classLoader) {
mixInCache.entrySet().removeIf(
entry -> entry.getKey().getClassLoader() == classLoader
@ -429,6 +599,13 @@ public class ObjectReaderProvider
BeanUtils.cleanupCache(classLoader);
}
/**
* Gets the ObjectReaderCreator used by this provider. If a context-specific creator
* is available, it will be returned; otherwise, the default creator for this provider
* will be returned.
*
* @return the ObjectReaderCreator
*/
public ObjectReaderCreator getCreator() {
ObjectReaderCreator contextCreator = JSONFactory.getContextReaderCreator();
if (contextCreator != null) {
@ -437,6 +614,14 @@ public class ObjectReaderProvider
return this.creator;
}
/**
* Constructs an ObjectReaderProvider with the default ObjectReaderCreator based on
* system configuration. The creator selection follows this priority:
* 1. ASM creator (default, if not Android or GraalVM)
* 2. Reflection/Lambda creator (fallback)
*
* <p>The provider is initialized with the base module and all registered modules.
*/
public ObjectReaderProvider() {
ObjectReaderCreator creator = null;
switch (JSONFactory.CREATOR) {
@ -464,6 +649,11 @@ public class ObjectReaderProvider
init();
}
/**
* Constructs an ObjectReaderProvider with the specified ObjectReaderCreator.
*
* @param creator the ObjectReaderCreator to use for creating ObjectReader instances
*/
public ObjectReaderProvider(ObjectReaderCreator creator) {
this.creator = creator;
modules.add(new ObjectReaderBaseModule(this));
@ -476,6 +666,13 @@ public class ObjectReaderProvider
}
}
/**
* Gets the type converter function that can convert values from one type to another.
*
* @param from the source type
* @param to the target type
* @return the converter function for the type pair, or null if no converter is registered
*/
public Function getTypeConvert(Type from, Type to) {
Map<Type, Function> map = typeConverts.get(from);
if (map == null) {
@ -484,6 +681,14 @@ public class ObjectReaderProvider
return map.get(to);
}
/**
* Registers a type converter function that can convert values from one type to another.
*
* @param from the source type
* @param to the target type
* @param typeConvert the function to convert from source type to target type
* @return the previous converter function for the type pair, or null if there was no previous converter
*/
public Function registerTypeConvert(Type from, Type to, Function typeConvert) {
Map<Type, Function> map = typeConverts.get(from);
if (map == null) {
@ -493,6 +698,13 @@ public class ObjectReaderProvider
return map.put(to, typeConvert);
}
/**
* Gets an ObjectReader by its hash code. This method first checks thread-local cache,
* then global cache for performance optimization.
*
* @param hashCode the hash code of the ObjectReader to retrieve
* @return the ObjectReader associated with the hash code, or null if not found
*/
public ObjectReader getObjectReader(long hashCode) {
ObjectReaderCachePair pair = readerCache;
if (pair != null) {
@ -527,6 +739,15 @@ public class ObjectReaderProvider
return objectReader;
}
/**
* Gets an ObjectReader for the specified type name, expected class, and features.
* This method handles auto-type resolution and ObjectReader caching.
*
* @param typeName the name of the type
* @param expectClass the expected class type
* @param features the JSON reader features
* @return the ObjectReader for the specified type, or null if the type cannot be resolved
*/
public ObjectReader getObjectReader(String typeName, Class<?> expectClass, long features) {
Class<?> autoTypeClass = checkAutoType(typeName, expectClass, features);
if (autoTypeClass == null) {
@ -551,6 +772,16 @@ public class ObjectReaderProvider
}
}
/**
* Checks and resolves the class for auto-type support. This method handles security
* validation and class loading for auto-type deserialization.
*
* @param typeName the name of the type to check
* @param expectClass the expected class type
* @param features the JSON reader features
* @return the resolved Class, or null if the type cannot be resolved or is not allowed
* @throws JSONException if the type is not supported or security checks fail
*/
public Class<?> checkAutoType(String typeName, Class<?> expectClass, long features) {
if (typeName == null || typeName.isEmpty()) {
return null;
@ -677,10 +908,21 @@ public class ObjectReaderProvider
return clazz;
}
/**
* Gets the list of registered ObjectReader modules.
*
* @return the list of modules
*/
public List<ObjectReaderModule> getModules() {
return modules;
}
/**
* Gets bean information for the specified class by delegating to registered modules.
*
* @param beanInfo the BeanInfo object to populate with bean information
* @param objectClass the class for which to get bean information
*/
public void getBeanInfo(BeanInfo beanInfo, Class objectClass) {
for (int i = 0; i < modules.size(); i++) {
ObjectReaderModule module = modules.get(i);
@ -688,6 +930,13 @@ public class ObjectReaderProvider
}
}
/**
* Gets field information for the specified field of a class.
*
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the field
* @param field the field for which to get information
*/
public void getFieldInfo(FieldInfo fieldInfo, Class objectClass, Field field) {
for (int i = 0; i < modules.size(); i++) {
ObjectReaderModule module = modules.get(i);
@ -695,6 +944,15 @@ public class ObjectReaderProvider
}
}
/**
* Gets field information for the specified constructor parameter.
*
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the constructor
* @param constructor the constructor containing the parameter
* @param paramIndex the index of the parameter in the constructor
* @param parameter the parameter for which to get information
*/
public void getFieldInfo(
FieldInfo fieldInfo,
Class objectClass,
@ -710,6 +968,15 @@ public class ObjectReaderProvider
}
}
/**
* Gets field information for the specified method parameter.
*
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the method
* @param method the method containing the parameter
* @param paramIndex the index of the parameter in the method
* @param parameter the parameter for which to get information
*/
public void getFieldInfo(
FieldInfo fieldInfo,
Class objectClass,
@ -724,10 +991,25 @@ public class ObjectReaderProvider
}
}
/**
* Gets an ObjectReader for the specified type. If an ObjectReader for the type
* is already cached, it will be returned directly. Otherwise, a new ObjectReader
* will be created and cached.
*
* @param objectType the type for which to get an ObjectReader
* @return the ObjectReader for the specified type
*/
public ObjectReader getObjectReader(Type objectType) {
return getObjectReader(objectType, false);
}
/**
* Creates a value consumer creator for byte array values.
*
* @param objectClass the class for which to create the value consumer creator
* @param fieldReaderArray the field readers to use
* @return a function that creates ByteArrayValueConsumer instances
*/
public Function<Consumer, ByteArrayValueConsumer> createValueConsumerCreator(
Class objectClass,
FieldReader[] fieldReaderArray
@ -735,6 +1017,13 @@ public class ObjectReaderProvider
return creator.createByteArrayValueConsumerCreator(objectClass, fieldReaderArray);
}
/**
* Creates a value consumer creator for char array values.
*
* @param objectClass the class for which to create the value consumer creator
* @param fieldReaderArray the field readers to use
* @return a function that creates CharArrayValueConsumer instances
*/
public Function<Consumer, CharArrayValueConsumer> createCharArrayValueConsumerCreator(
Class objectClass,
FieldReader[] fieldReaderArray
@ -742,6 +1031,15 @@ public class ObjectReaderProvider
return creator.createCharArrayValueConsumerCreator(objectClass, fieldReaderArray);
}
/**
* Gets an ObjectReader for the specified type with field-based option.
* If an ObjectReader for the type is already cached, it will be returned directly.
* Otherwise, a new ObjectReader will be created and cached.
*
* @param objectType the type for which to get an ObjectReader
* @param fieldBased whether to use field-based reading (true) or method-based reading (false)
* @return the ObjectReader for the specified type
*/
public ObjectReader getObjectReader(Type objectType, boolean fieldBased) {
if (objectType == null) {
objectType = Object.class;
@ -860,14 +1158,20 @@ public class ObjectReaderProvider
: cache.putIfAbsent(objectType, boundObjectReader);
}
/**
* Gets the auto-type before handler that is invoked before type resolution.
*
* @return the auto-type before handler, or null if none is set
*/
public AutoTypeBeforeHandler getAutoTypeBeforeHandler() {
return autoTypeBeforeHandler;
}
public Map<String, Date> getAutoTypeList() {
return autoTypeList;
}
/**
* Sets the auto-type before handler that will be invoked before type resolution.
*
* @param autoTypeBeforeHandler the auto-type before handler to set
*/
public void setAutoTypeBeforeHandler(AutoTypeBeforeHandler autoTypeBeforeHandler) {
this.autoTypeBeforeHandler = autoTypeBeforeHandler;
}
@ -887,6 +1191,14 @@ public class ObjectReaderProvider
}
}
/**
* Gets field information for the specified method of a class. This method also
* handles setter methods by attempting to find corresponding fields.
*
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the method
* @param method the method for which to get information
*/
public void getFieldInfo(FieldInfo fieldInfo, Class objectClass, Method method) {
for (int i = 0; i < modules.size(); i++) {
ObjectReaderAnnotationProcessor annotationProcessor = modules.get(i).getAnnotationProcessor();
@ -908,6 +1220,15 @@ public class ObjectReaderProvider
}
}
/**
* Creates an object creator (supplier) for the specified class and reader features.
*
* @param objectClass the class for which to create an object creator
* @param readerFeatures the reader features to use
* @param <T> the type of the object
* @return a supplier function that creates new instances of the object
* @throws JSONException if no default constructor is found for the class
*/
public <T> Supplier<T> createObjectCreator(Class<T> objectClass, long readerFeatures) {
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = fieldBased
@ -925,6 +1246,14 @@ public class ObjectReaderProvider
return LambdaMiscCodec.createSupplier(constructor);
}
/**
* Creates a FieldReader for the specified class, field name, and reader features.
*
* @param objectClass the class containing the field
* @param fieldName the name of the field
* @param readerFeatures the reader features to use
* @return a FieldReader for the specified field, or null if the field is not found
*/
public FieldReader createFieldReader(Class objectClass, String fieldName, long readerFeatures) {
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
@ -967,6 +1296,16 @@ public class ObjectReaderProvider
return null;
}
/**
* Creates an ObjectReader for a custom object with specified field names, types, and consumer.
*
* @param names the field names
* @param types the field types
* @param supplier the supplier function to create new instances of the object
* @param c the field consumer to set field values
* @param <T> the type of the object
* @return the created ObjectReader
*/
public <T> ObjectReader<T> createObjectReader(
String[] names,
Type[] types,
@ -976,6 +1315,17 @@ public class ObjectReaderProvider
return createObjectReader(names, types, null, supplier, c);
}
/**
* Creates an ObjectReader for a custom object with specified field names, types, features, and consumer.
*
* @param names the field names
* @param types the field types
* @param features the field features (can be null)
* @param supplier the supplier function to create new instances of the object
* @param c the field consumer to set field values
* @param <T> the type of the object
* @return the created ObjectReader
*/
public <T> ObjectReader<T> createObjectReader(
String[] names,
Type[] types,
@ -1004,47 +1354,100 @@ public class ObjectReaderProvider
);
}
/**
* Checks if reference detection is disabled.
*
* @return true if reference detection is disabled, false otherwise
*/
public boolean isDisableReferenceDetect() {
return disableReferenceDetect;
}
/**
* Checks if auto-type support is disabled.
*
* @return true if auto-type support is disabled, false otherwise
*/
public boolean isDisableAutoType() {
return disableAutoType;
}
/**
* Checks if JSONB support is disabled.
*
* @return true if JSONB support is disabled, false otherwise
*/
public boolean isDisableJSONB() {
return disableJSONB;
}
/**
* Checks if array mapping is disabled.
*
* @return true if array mapping is disabled, false otherwise
*/
public boolean isDisableArrayMapping() {
return disableArrayMapping;
}
/**
* Sets whether reference detection is disabled.
*
* @param disableReferenceDetect true to disable reference detection, false to enable it
*/
public void setDisableReferenceDetect(boolean disableReferenceDetect) {
this.disableReferenceDetect = disableReferenceDetect;
}
/**
* Sets whether array mapping is disabled.
*
* @param disableArrayMapping true to disable array mapping, false to enable it
*/
public void setDisableArrayMapping(boolean disableArrayMapping) {
this.disableArrayMapping = disableArrayMapping;
}
/**
* Sets whether JSONB support is disabled.
*
* @param disableJSONB true to disable JSONB support, false to enable it
*/
public void setDisableJSONB(boolean disableJSONB) {
this.disableJSONB = disableJSONB;
}
/**
* Sets whether auto-type support is disabled.
*
* @param disableAutoType true to disable auto-type support, false to enable it
*/
public void setDisableAutoType(boolean disableAutoType) {
this.disableAutoType = disableAutoType;
}
/**
* Checks if smart match is disabled.
*
* @return true if smart match is disabled, false otherwise
*/
public boolean isDisableSmartMatch() {
return disableSmartMatch;
}
/**
* Sets whether smart match is disabled.
*
* @param disableSmartMatch true to disable smart match, false to enable it
*/
public void setDisableSmartMatch(boolean disableSmartMatch) {
this.disableSmartMatch = disableSmartMatch;
}
/**
* Gets the property naming strategy used by this provider.
*
* @return the property naming strategy, or null if none is set
* @since 2.0.52
*/
public PropertyNamingStrategy getNamingStrategy() {
@ -1052,6 +1455,9 @@ public class ObjectReaderProvider
}
/**
* Sets the property naming strategy used by this provider.
*
* @param namingStrategy the property naming strategy to set
* @since 2.0.52
*/
public void setNamingStrategy(PropertyNamingStrategy namingStrategy) {

View File

@ -19,6 +19,33 @@ import static com.alibaba.fastjson2.util.IOUtils.*;
import static com.alibaba.fastjson2.util.JDKUtils.*;
import static java.time.ZoneOffset.UTC;
/**
* DateUtils provides utility methods for parsing and formatting dates in various formats.
* It supports multiple date formats including ISO 8601, RFC formats, and custom patterns.
*
* <p>This class offers functionality for:
* <ul>
* <li>Parsing date strings in various formats to Date objects</li>
* <li>Formatting Date objects to date strings</li>
* <li>Working with different time zones and locales</li>
* <li>Handling special date formats like cookies and HTTP headers</li>
* <li>Converting between different date representations</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Parse a date string
* Date date = DateUtils.parseDate("2023-12-25 10:30:45");
*
* // Format a Date object
* String formatted = DateUtils.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
*
* // Parse with specific format and time zone
* Date date2 = DateUtils.parseDate("2023/12/25 10:30:45", "yyyy/MM/dd HH:mm:ss", ZoneId.of("UTC"));
* </pre>
*
* @since 2.0.0
*/
public class DateUtils {
public static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();
public static final String SHANGHAI_ZONE_ID_NAME = "Asia/Shanghai";
@ -75,6 +102,13 @@ public class DateUtils {
static final String[] CACHE = new String[1024];
}
/**
* Parses a date string in the format "yyyy-MM-dd HH:mm:ss" to a Date object.
* Uses the default time zone for parsing.
*
* @param str the date string to parse, e.g., "2023-12-25 10:30:45"
* @return the parsed Date object, or null if the input string is null or empty
*/
public static Date parseDateYMDHMS19(String str) {
if (str == null || str.isEmpty()) {
return null;
@ -84,10 +118,26 @@ public class DateUtils {
return new Date(millis);
}
/**
* Parses a date string with the specified format to a Date object.
* Uses the default time zone for parsing.
*
* @param str the date string to parse
* @param format the format pattern to use for parsing
* @return the parsed Date object, or null if the input string is null, empty, or "null"
*/
public static Date parseDate(String str, String format) {
return parseDate(str, format, DEFAULT_ZONE_ID);
}
/**
* Parses a date string with the specified format and time zone to a Date object.
*
* @param str the date string to parse
* @param format the format pattern to use for parsing
* @param zoneId the time zone to use for parsing
* @return the parsed Date object, or null if the input string is null, empty, or "null"
*/
public static Date parseDate(String str, String format, ZoneId zoneId) {
if (str == null || str.isEmpty() || "null".equals(str)) {
return null;
@ -163,6 +213,13 @@ public class DateUtils {
return new Date(millis);
}
/**
* Parses a date string to a Date object using default parsing rules.
* Uses the default time zone for parsing.
*
* @param str the date string to parse
* @return the parsed Date object, or null if the input string is null, empty, or "null"
*/
public static Date parseDate(String str) {
long millis = parseMillis(str, DEFAULT_ZONE_ID);
if (millis == 0) {
@ -171,6 +228,14 @@ public class DateUtils {
return new Date(millis);
}
/**
* Parses a date string to a Date object using default parsing rules.
* Uses the specified time zone for parsing.
*
* @param str the date string to parse
* @param zoneId the time zone to use for parsing
* @return the parsed Date object, or null if the input string is null, empty, or "null"
*/
public static Date parseDate(String str, ZoneId zoneId) {
long millis = parseMillis(str, zoneId);
if (millis == 0) {
@ -179,10 +244,25 @@ public class DateUtils {
return new Date(millis);
}
/**
* Parses a date string to milliseconds since epoch using default parsing rules.
* Uses the default time zone for parsing.
*
* @param str the date string to parse
* @return the parsed milliseconds since epoch, or 0 if the input string is null
*/
public static long parseMillis(String str) {
return parseMillis(str, DEFAULT_ZONE_ID);
}
/**
* Parses a date string to milliseconds since epoch using default parsing rules.
* Uses the specified time zone for parsing.
*
* @param str the date string to parse
* @param zoneId the time zone to use for parsing
* @return the parsed milliseconds since epoch, or 0 if the input string is null
*/
public static long parseMillis(String str, ZoneId zoneId) {
if (str == null) {
return 0;
@ -197,6 +277,12 @@ public class DateUtils {
return parseMillis(chars, 0, chars.length, zoneId);
}
/**
* Parses a date string to a LocalDateTime object using default parsing rules.
*
* @param str the date string to parse
* @return the parsed LocalDateTime object, or null if the input string is null or empty
*/
public static LocalDateTime parseLocalDateTime(String str) {
if (str == null) {
return null;
@ -204,6 +290,15 @@ public class DateUtils {
return parseLocalDateTime(str, 0, str.length());
}
/**
* Parses a date string to a LocalDateTime object using default parsing rules
* with specified offset and length.
*
* @param str the date string to parse
* @param off the offset in the string to start parsing from
* @param len the length of the substring to parse
* @return the parsed LocalDateTime object, or null if the input string is null or empty
*/
public static LocalDateTime parseLocalDateTime(String str, int off, int len) {
if (str == null || len == 0) {
return null;
@ -241,6 +336,24 @@ public class DateUtils {
return ldt;
}
/**
* Parses a character array to a LocalDateTime object using default parsing rules
* with specified offset and length.
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @param len the length of the subarray to parse
* @return the parsed LocalDateTime object, or null if the input array is null or empty
*/
/**
* Parses a character array to a LocalDateTime object using default parsing rules
* with specified offset and length.
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @param len the length of the subarray to parse
* @return the parsed LocalDateTime object, or null if the input array is null or empty
*/
public static LocalDateTime parseLocalDateTime(char[] str, int off, int len) {
if (str == null || len == 0) {
return null;
@ -304,6 +417,14 @@ public class DateUtils {
}
}
/**
* Parses a byte array to a LocalTime object with 5-character format (HH:mm).
* Supports formats like "10:30".
*
* @param str the byte array to parse
* @param off the offset in the array to start parsing from
* @return the parsed LocalTime object, or null if the input array is null or too short
*/
public static LocalTime parseLocalTime5(byte[] str, int off) {
if (off + 5 > str.length) {
return null;
@ -325,6 +446,14 @@ public class DateUtils {
return localTime(hour, minute, second);
}
/**
* Parses a character array to a LocalTime object with 5-character format (HH:mm).
* Supports formats like "10:30".
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @return the parsed LocalTime object, or null if the input array is null or too short
*/
public static LocalTime parseLocalTime5(char[] str, int off) {
if (off + 5 > str.length) {
return null;
@ -345,6 +474,14 @@ public class DateUtils {
return localTime(hour, minute, second);
}
/**
* Parses a byte array to a LocalTime object with 6-character format (HHmmss).
* Supports formats like "103045".
*
* @param str the byte array to parse
* @param off the offset in the array to start parsing from
* @return the parsed LocalTime object, or null if the input array is null or too short
*/
public static LocalTime parseLocalTime6(byte[] str, int off) {
if (off + 5 > str.length) {
return null;
@ -373,6 +510,14 @@ public class DateUtils {
return localTime(hour, minute, second);
}
/**
* Parses a character array to a LocalTime object with 6-character format (HHmmss).
* Supports formats like "103045".
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @return the parsed LocalTime object, or null if the input array is null or too short
*/
public static LocalTime parseLocalTime6(char[] str, int off) {
if (off + 5 > str.length) {
return null;
@ -795,6 +940,15 @@ public class DateUtils {
}
}
/**
* Parses a character array to a LocalDate object using default parsing rules
* with specified offset and length.
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @param len the length of the subarray to parse
* @return the parsed LocalDate object, or null if the input array is null or empty
*/
public static LocalDate parseLocalDate(char[] str, int off, int len) {
if (str == null || len == 0) {
return null;
@ -3030,6 +3184,15 @@ public class DateUtils {
return parseZonedDateTime(str, off, len, DEFAULT_ZONE_ID);
}
/**
* Parses a character array to a ZonedDateTime object using default parsing rules
* with specified offset and length.
*
* @param str the character array to parse
* @param off the offset in the array to start parsing from
* @param len the length of the subarray to parse
* @return the parsed ZonedDateTime object, or null if the input array is null or empty
*/
public static ZonedDateTime parseZonedDateTime(char[] str, int off, int len) {
return parseZonedDateTime(str, off, len, DEFAULT_ZONE_ID);
}
@ -7571,10 +7734,25 @@ public class DateUtils {
+ second;
}
/**
* Formats a Date object to a string in the format "yyyy-MM-dd HH:mm:ss".
* Uses the system default time zone for formatting.
*
* @param date the Date object to format
* @return the formatted date string, e.g., "2023-12-25 10:30:45"
*/
public static String formatYMDHMS19(Date date) {
return formatYMDHMS19(date, DEFAULT_ZONE_ID);
}
/**
* Formats a Date object to a string in the format "yyyy-MM-dd HH:mm:ss".
* Uses the specified time zone for formatting.
*
* @param date the Date object to format
* @param zoneId the time zone to use for formatting
* @return the formatted date string, e.g., "2023-12-25 10:30:45"
*/
public static String formatYMDHMS19(Date date, ZoneId zoneId) {
if (date == null) {
return null;
@ -7682,6 +7860,13 @@ public class DateUtils {
return new String(chars);
}
/**
* Formats a Date object to a string in the format "yyyyMMdd".
* Uses the system default time zone for formatting.
*
* @param date the Date object to format
* @return the formatted date string, e.g., "20231225"
*/
public static String formatYMD8(Date date) {
if (date == null) {
return null;
@ -7690,6 +7875,14 @@ public class DateUtils {
return formatYMD8(date.getTime(), DEFAULT_ZONE_ID);
}
/**
* Formats a timestamp in milliseconds to a string in the format "yyyyMMdd".
* Uses the specified time zone for formatting.
*
* @param timeMillis the timestamp in milliseconds to format
* @param zoneId the time zone to use for formatting
* @return the formatted date string, e.g., "20231225"
*/
public static String formatYMD8(long timeMillis, ZoneId zoneId) {
final long SECONDS_PER_DAY = 60 * 60 * 24;

File diff suppressed because it is too large Load Diff

View File

@ -8,19 +8,76 @@ import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
/**
* ObjectWriter is responsible for serializing Java objects into JSON format.
* It provides a set of methods for writing objects to JSON, handling field mapping,
* and supporting various serialization features.
*
* <p>This interface supports various features including:
* <ul>
* <li>Object serialization to JSON and JSONB formats</li>
* <li>Field mapping and value extraction</li>
* <li>Array mapping serialization</li>
* <li>Type information writing</li>
* <li>Custom feature configuration</li>
* <li>Filter support for property filtering</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get an ObjectWriter for a specific type
* ObjectWriter&lt;User&gt; writer = JSONFactory.getDefaultObjectWriterProvider().getObjectWriter(User.class);
*
* // Write to JSON string
* User user = new User(1, "John");
* String jsonString = writer.toJSONString(user);
*
* // Write to JSONWriter
* try (JSONWriter jsonWriter = JSONWriter.of()) {
* writer.write(jsonWriter, user);
* String result = jsonWriter.toString();
* }
* </pre>
*
* @param <T> the type of objects that this ObjectWriter can serialize
* @since 2.0.0
*/
public interface ObjectWriter<T> {
/**
* Gets the features enabled by this ObjectWriter.
*
* @return the enabled features as a bit mask
*/
default long getFeatures() {
return 0;
}
/**
* Gets the list of FieldWriters associated with this ObjectWriter.
*
* @return the list of FieldWriters, or an empty list if none are available
*/
default List<FieldWriter> getFieldWriters() {
return Collections.emptyList();
}
/**
* Gets the FieldWriter for the specified field hash code.
*
* @param hashCode the hash code of the field name
* @return the FieldWriter for the field, or null if not found
*/
default FieldWriter getFieldWriter(long hashCode) {
return null;
}
/**
* Gets the value of a field from the specified object.
*
* @param object the object from which to get the field value
* @param fieldName the name of the field to get
* @return the value of the field, or null if the field is not found
*/
default Object getFieldValue(Object object, String fieldName) {
FieldWriter fieldWriter = getFieldWriter(fieldName);
if (fieldWriter == null) {
@ -29,6 +86,12 @@ public interface ObjectWriter<T> {
return fieldWriter.getFieldValue(object);
}
/**
* Gets the FieldWriter for the specified field name.
*
* @param name the name of the field
* @return the FieldWriter for the field, or null if not found
*/
default FieldWriter getFieldWriter(String name) {
long nameHash = Fnv.hashCode64(name);
FieldWriter fieldWriter = getFieldWriter(nameHash);
@ -41,18 +104,49 @@ public interface ObjectWriter<T> {
return fieldWriter;
}
/**
* Writes type information to the JSON output. This is used for polymorphic serialization
* to include type information in the JSON output.
*
* @param jsonWriter the JSONWriter to which type information should be written
* @return true if type information was written, false otherwise
*/
default boolean writeTypeInfo(JSONWriter jsonWriter) {
return false;
}
/**
* Writes an object to the JSONWriter in JSONB format.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
* @param fieldName the name of the field being written
* @param fieldType the type of the field being written
* @param features the features to use for writing
*/
default void writeJSONB(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
write(jsonWriter, object, fieldName, fieldType, features);
}
/**
* Writes an object to the JSONWriter in array mapping JSONB format.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
*/
default void writeArrayMappingJSONB(JSONWriter jsonWriter, Object object) {
writeArrayMappingJSONB(jsonWriter, object, null, null, 0);
}
/**
* Writes an object to the JSONWriter in array mapping JSONB format with additional parameters.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
* @param fieldName the name of the field being written
* @param fieldType the type of the field being written
* @param features the features to use for writing
*/
default void writeArrayMappingJSONB(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
List<FieldWriter> fieldWriters = getFieldWriters();
int size = fieldWriters.size();
@ -63,6 +157,15 @@ public interface ObjectWriter<T> {
}
}
/**
* Writes an object to the JSONWriter in array mapping format.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
* @param fieldName the name of the field being written
* @param fieldType the type of the field being written
* @param features the features to use for writing
*/
default void writeArrayMapping(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
if (jsonWriter.jsonb) {
writeArrayMappingJSONB(jsonWriter, object, fieldName, fieldType, features);
@ -126,14 +229,33 @@ public interface ObjectWriter<T> {
jsonWriter.endArray();
}
/**
* Checks if the JSONWriter has any filters enabled that would affect serialization.
*
* @param jsonWriter the JSONWriter to check
* @return true if filters are enabled, false otherwise
*/
default boolean hasFilter(JSONWriter jsonWriter) {
return jsonWriter.hasFilter(JSONWriter.Feature.IgnoreNonFieldGetter.mask);
}
/**
* Writes an object to the JSONWriter using default parameters.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
*/
default void write(JSONWriter jsonWriter, Object object) {
write(jsonWriter, object, null, null, 0);
}
/**
* Converts an object to its JSON string representation using the specified features.
*
* @param object the object to convert to JSON
* @param features the JSON writer features to use
* @return the JSON string representation of the object
*/
default String toJSONString(T object, JSONWriter.Feature... features) {
try (JSONWriter jsonWriter = JSONWriter.of(features)) {
write(jsonWriter, object, null, null, 0);
@ -141,28 +263,79 @@ public interface ObjectWriter<T> {
}
}
/**
* Writes an object to the JSONWriter with the given field name, field type, and features.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
* @param fieldName the name of the field being written
* @param fieldType the type of the field being written
* @param features the features to use for writing
*/
void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features);
/**
* Writes an object to the JSONWriter with filter support using default parameters.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
*/
default void writeWithFilter(JSONWriter jsonWriter, Object object) {
writeWithFilter(jsonWriter, object, null, null, 0);
}
/**
* Writes an object to the JSONWriter with filter support.
*
* @param jsonWriter the JSONWriter to which the object should be written
* @param object the object to write
* @param fieldName the name of the field being written
* @param fieldType the type of the field being written
* @param features the features to use for writing
* @throws UnsupportedOperationException if the method is not implemented
*/
default void writeWithFilter(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
throw new UnsupportedOperationException();
}
/**
* Sets the property filter for this ObjectWriter.
*
* @param propertyFilter the property filter to set
*/
default void setPropertyFilter(PropertyFilter propertyFilter) {
}
/**
* Sets the value filter for this ObjectWriter.
*
* @param valueFilter the value filter to set
*/
default void setValueFilter(ValueFilter valueFilter) {
}
/**
* Sets the name filter for this ObjectWriter.
*
* @param nameFilter the name filter to set
*/
default void setNameFilter(NameFilter nameFilter) {
}
/**
* Sets the property pre-filter for this ObjectWriter.
*
* @param propertyPreFilter the property pre-filter to set
*/
default void setPropertyPreFilter(PropertyPreFilter propertyPreFilter) {
}
/**
* Sets a filter for this ObjectWriter. The filter type is determined at runtime
* and the appropriate setter method is called.
*
* @param filter the filter to set
*/
default void setFilter(Filter filter) {
if (filter instanceof PropertyFilter) {
setPropertyFilter((PropertyFilter) filter);

View File

@ -27,6 +27,35 @@ import static com.alibaba.fastjson2.util.BeanUtils.SUPER;
import static com.alibaba.fastjson2.util.TypeUtils.*;
import static com.alibaba.fastjson2.writer.ObjectWriterProvider.NAME_COMPATIBLE_WITH_FILED;
/**
* ObjectWriterCreator is responsible for creating ObjectWriter instances for
* serializing Java objects into JSON format. It provides factory methods for
* creating ObjectWriters for various types of objects and fields.
*
* <p>This class supports various features including:
* <ul>
* <li>Creation of ObjectWriters for different object types</li>
* <li>Creation of FieldWriters for different field types</li>
* <li>Lambda expression support for getter methods</li>
* <li>Custom field writer creation with various configurations</li>
* <li>JIT compilation support for improved performance</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get default creator
* ObjectWriterCreator creator = JSONFactory.getDefaultObjectWriterCreator();
*
* // Create ObjectWriter for a class
* ObjectWriter&lt;User&gt; writer = creator.createObjectWriter(User.class);
*
* // Create FieldWriter for a field
* Field field = User.class.getDeclaredField("name");
* FieldWriter&lt;User&gt; fieldWriter = creator.createFieldWriter("name", null, field);
* </pre>
*
* @since 2.0.0
*/
public class ObjectWriterCreator {
public static final ObjectWriterCreator INSTANCE = new ObjectWriterCreator();
@ -46,17 +75,41 @@ public class ObjectWriterCreator {
protected final AtomicInteger jitErrorCount = new AtomicInteger();
protected volatile Throwable jitErrorLast;
/**
* Constructs a new ObjectWriterCreator instance.
*/
public ObjectWriterCreator() {
}
/**
* Creates an ObjectWriter for the specified list of FieldWriters.
*
* @param fieldWriters the list of FieldWriters to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(List<FieldWriter> fieldWriters) {
return new ObjectWriterAdapter(null, null, null, 0, fieldWriters);
}
/**
* Creates an ObjectWriter for the specified array of FieldWriters.
*
* @param fieldWriters the array of FieldWriters to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(FieldWriter... fieldWriters) {
return createObjectWriter(Arrays.asList(fieldWriters));
}
/**
* Creates an ObjectWriter for the specified object type with names, types, and supplier.
*
* @param <T> the type of objects that the ObjectWriter can serialize
* @param names the field names
* @param types the field types
* @param supplier the FieldSupplier to use
* @return an ObjectWriter instance
*/
public <T> ObjectWriter<T> createObjectWriter(String[] names, Type[] types, FieldSupplier<T> supplier) {
FieldWriter[] fieldWriters = new FieldWriter[names.length];
for (int i = 0; i < names.length; i++) {
@ -68,6 +121,12 @@ public class ObjectWriterCreator {
return createObjectWriter(fieldWriters);
}
/**
* Creates an ObjectWriter for the specified object type.
*
* @param objectType the class of objects to serialize
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(Class objectType) {
return createObjectWriter(
objectType,
@ -76,11 +135,26 @@ public class ObjectWriterCreator {
);
}
/**
* Creates an ObjectWriter for the specified object type and field writers.
*
* @param objectType the class of objects to serialize
* @param fieldWriters the field writers to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(Class objectType,
FieldWriter... fieldWriters) {
return createObjectWriter(objectType, 0, fieldWriters);
}
/**
* Creates an ObjectWriter for the specified object class, features, and field writers.
*
* @param objectClass the class of objects to serialize
* @param features the features to use
* @param fieldWriters the field writers to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(
Class objectClass,
long features,
@ -135,6 +209,18 @@ public class ObjectWriterCreator {
return new ObjectWriterAdapter(objectClass, null, null, features, Arrays.asList(fieldWriters));
}
/**
* Creates a FieldWriter for the specified field.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param objectClass the class containing the field
* @param writerFeatures the writer features to use
* @param provider the ObjectWriterProvider to use
* @param beanInfo the BeanInfo to use
* @param fieldInfo the FieldInfo to use
* @param field the Field to create a writer for
* @return a FieldWriter instance, or null if the field should be ignored
*/
protected FieldWriter createFieldWriter(
Class objectClass,
long writerFeatures,
@ -271,6 +357,14 @@ public class ObjectWriterCreator {
fieldInfo.contentAs);
}
/**
* Creates an ObjectWriter for the specified object class, features, and modules.
*
* @param objectClass the class of objects to serialize
* @param features the features to use
* @param modules the ObjectWriterModules to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(
Class objectClass,
long features,
@ -285,6 +379,12 @@ public class ObjectWriterCreator {
return createObjectWriter(objectClass, features, provider);
}
/**
* Sets default values for the specified field writers using the default constructor of the object class.
*
* @param fieldWriters the list of FieldWriters to set default values for
* @param objectClass the class of objects to create default instances from
*/
protected void setDefaultValue(List<FieldWriter> fieldWriters, Class objectClass) {
Constructor constructor = BeanUtils.getDefaultConstructor(objectClass, true);
if (constructor == null) {
@ -312,6 +412,16 @@ public class ObjectWriterCreator {
}
}
/**
* Creates an ObjectWriter for the specified object class, features, and provider.
* This is the main method for creating ObjectWriters that handles all the complexity
* of analyzing the class structure and creating appropriate FieldWriters.
*
* @param objectClass the class of objects to serialize
* @param features the features to use
* @param provider the ObjectWriterProvider to use
* @return an ObjectWriter instance
*/
public ObjectWriter createObjectWriter(
final Class objectClass,
final long features,
@ -618,6 +728,17 @@ public class ObjectWriterCreator {
return writerAdapter;
}
/**
* Gets the field name for the specified method based on various naming strategies and configurations.
*
* @param objectClass the class containing the method
* @param provider the ObjectWriterProvider to use
* @param beanInfo the BeanInfo containing configuration
* @param record whether the class is a record
* @param fieldInfo the FieldInfo containing field configuration
* @param method the method to get the field name for
* @return the field name
*/
protected static String getFieldName(
Class objectClass,
ObjectWriterProvider provider,
@ -668,6 +789,12 @@ public class ObjectWriterCreator {
return fieldName;
}
/**
* Configures serialize filters for the specified ObjectWriterAdapter.
*
* @param beanInfo the BeanInfo containing filter configuration
* @param writerAdapter the ObjectWriterAdapter to configure filters for
*/
protected static void configSerializeFilters(BeanInfo beanInfo, ObjectWriterAdapter writerAdapter) {
for (Class<? extends Filter> filterClass : beanInfo.serializeFilters) {
if (!Filter.class.isAssignableFrom(filterClass)) {
@ -683,6 +810,12 @@ public class ObjectWriterCreator {
}
}
/**
* Handles field ignores based on the BeanInfo configuration.
*
* @param beanInfo the BeanInfo containing ignore configuration
* @param fieldWriters the list of FieldWriters to process
*/
protected void handleIgnores(BeanInfo beanInfo, List<FieldWriter> fieldWriters) {
if (beanInfo.ignores == null || beanInfo.ignores.length == 0) {
return;
@ -699,10 +832,30 @@ public class ObjectWriterCreator {
}
}
/**
* Creates a FieldWriter for the specified field with default configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param format the date format to use
* @param field the Field to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(String fieldName, String format, Field field) {
return createFieldWriter(JSONFactory.getDefaultObjectWriterProvider(), fieldName, 0, 0L, format, null, field, null);
}
/**
* Creates a FieldWriter for the specified field with ordinal and features.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param field the Field to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
String fieldName,
int ordinal,
@ -713,6 +866,19 @@ public class ObjectWriterCreator {
return createFieldWriter(JSONFactory.getDefaultObjectWriterProvider(), fieldName, ordinal, features, format, null, field, null);
}
/**
* Creates a FieldWriter for the specified field with comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param label the label for the field
* @param field the Field to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
String fieldName,
int ordinal,
@ -725,6 +891,20 @@ public class ObjectWriterCreator {
return createFieldWriter(JSONFactory.getDefaultObjectWriterProvider(), fieldName, ordinal, features, format, label, field, initObjectWriter);
}
/**
* Creates a FieldWriter for the specified field with provider and comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param label the label for the field
* @param field the Field to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @return a FieldWriter instance
*/
public final <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
String fieldName,
@ -748,6 +928,21 @@ public class ObjectWriterCreator {
);
}
/**
* Creates a FieldWriter for the specified field with locale and comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param field the Field to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
String fieldName,
@ -773,6 +968,22 @@ public class ObjectWriterCreator {
);
}
/**
* Creates a FieldWriter for the specified field with contentAs and comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param field the Field to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @param contentAs the contentAs class
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
String fieldName,
@ -926,6 +1137,16 @@ public class ObjectWriterCreator {
return new FieldWriterObject(fieldName, ordinal, features, format, locale, label, field.getGenericType(), fieldClass, field, null);
}
/**
* Creates a FieldWriter for the specified method with default configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param objectType the class containing the method
* @param fieldName the name of the field
* @param dateFormat the date format to use
* @param method the Method to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(Class<T> objectType,
String fieldName,
String dateFormat,
@ -943,6 +1164,21 @@ public class ObjectWriterCreator {
return createFieldWriter(null, objectType, fieldName, ordinal, features, format, null, method, null);
}
/**
* Creates a FieldWriter for the specified method with comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param objectType the class containing the method
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param label the label for the field
* @param method the Method to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectType,
@ -968,6 +1204,22 @@ public class ObjectWriterCreator {
);
}
/**
* Creates a FieldWriter for the specified method with locale and comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param objectType the class containing the method
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param method the Method to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectType,
@ -983,6 +1235,23 @@ public class ObjectWriterCreator {
return createFieldWriter(provider, objectType, fieldName, ordinal, features, format, locale, label, method, initObjectWriter, null);
}
/**
* Creates a FieldWriter for the specified method with contentAs and comprehensive configuration.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param provider the ObjectWriterProvider to use
* @param objectType the class containing the method
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param method the Method to create a writer for
* @param initObjectWriter the initial ObjectWriter to use
* @param contentAs the contentAs class
* @return a FieldWriter instance
*/
public <T> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectType,
@ -1108,42 +1377,126 @@ public class ObjectWriterCreator {
return new FieldWriterObjectMethod(fieldName, ordinal, features, format, locale, label, fieldType, fieldClass, null, method);
}
/**
* Creates a FieldWriter for the specified function that returns a long value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToLongFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToLongFunction<T> function) {
return new FieldWriterInt64ValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function that returns an int value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToIntFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToIntFunction<T> function) {
return new FieldWriterInt32ValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified field, method, and function that returns an int value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param field the Field to create a writer for
* @param method the Method to create a writer for
* @param function the ToIntFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, Field field, Method method, ToIntFunction<T> function) {
return new FieldWriterInt32ValFunc(fieldName, 0, 0, null, null, field, method, function);
}
/**
* Creates a FieldWriter for the specified function that returns a short value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToShortFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToShortFunction<T> function) {
return new FieldWriterInt16ValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function that returns a byte value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToByteFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToByteFunction<T> function) {
return new FieldWriterInt8ValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function that returns a float value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToFloatFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToFloatFunction<T> function) {
return new FieldWriterFloatValueFunc(fieldName, 0, 0L, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function that returns a double value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToDoubleFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToDoubleFunction<T> function) {
return new FieldWriterDoubleValueFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function that returns a char value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the ToCharFunction to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, ToCharFunction<T> function) {
return new FieldWriterCharValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified predicate function that returns a boolean value.
*
* @param <T> the type of objects that the FieldWriter can serialize
* @param fieldName the name of the field
* @param function the Predicate to create a writer for
* @return a FieldWriter instance
*/
public <T> FieldWriter createFieldWriter(String fieldName, Predicate<T> function) {
return new FieldWriterBoolValFunc(fieldName, 0, 0, null, null, null, null, function);
}
/**
* Creates a FieldWriter for the specified function with default configuration.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param fieldName the name of the field
* @param fieldClass the class of the field
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter createFieldWriter(
String fieldName,
Class fieldClass,
@ -1152,6 +1505,18 @@ public class ObjectWriterCreator {
return createFieldWriter(null, null, fieldName, 0, 0, null, null, fieldClass, fieldClass, null, function);
}
/**
* Creates a FieldWriter for the specified field, method, and function.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param fieldName the name of the field
* @param fieldClass the class of the field
* @param field the Field to create a writer for
* @param method the Method to create a writer for
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter createFieldWriter(
String fieldName,
Class fieldClass,
@ -1162,6 +1527,17 @@ public class ObjectWriterCreator {
return createFieldWriter(null, null, fieldName, 0, 0, null, null, fieldClass, fieldClass, field, method, function);
}
/**
* Creates a FieldWriter for the specified function with field type and class.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param fieldName the name of the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter createFieldWriter(
String fieldName,
Type fieldType,
@ -1171,6 +1547,18 @@ public class ObjectWriterCreator {
return createFieldWriter(null, null, fieldName, 0, 0, null, null, fieldType, fieldClass, null, function);
}
/**
* Creates a FieldWriter for the specified function with features and format.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param fieldName the name of the field
* @param features the features to use
* @param format the date format to use
* @param fieldClass the class of the field
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter createFieldWriter(
String fieldName,
long features,
@ -1181,6 +1569,24 @@ public class ObjectWriterCreator {
return createFieldWriter(null, null, fieldName, 0, features, format, null, fieldClass, fieldClass, null, function);
}
/**
* Creates a FieldWriter for the specified function with provider, object class, and comprehensive configuration.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param provider the ObjectWriterProvider to use
* @param objectClass the class containing the field
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param label the label for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param method the Method to create a writer for
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectClass,
@ -1198,6 +1604,25 @@ public class ObjectWriterCreator {
provider, objectClass, fieldName, ordinal, features, format, null, label, fieldType, fieldClass, null, method, function);
}
/**
* Creates a FieldWriter for the specified function with provider, object class, field, method, and comprehensive configuration.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param provider the ObjectWriterProvider to use
* @param objectClass the class containing the field
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param label the label for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param field the Field to create a writer for
* @param method the Method to create a writer for
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectClass,
@ -1229,6 +1654,26 @@ public class ObjectWriterCreator {
);
}
/**
* Creates a FieldWriter for the specified function with provider, object class, locale, and comprehensive configuration.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param provider the ObjectWriterProvider to use
* @param objectClass the class containing the field
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param field the Field to create a writer for
* @param method the Method to create a writer for
* @param function the Function to create a writer for
* @return a FieldWriter instance
*/
public <T, V> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectClass,
@ -1262,6 +1707,27 @@ public class ObjectWriterCreator {
);
}
/**
* Creates a FieldWriter for the specified function with provider, object class, contentAs, and comprehensive configuration.
*
* @param <T> the type of objects that owns the field
* @param <V> the type of field values
* @param provider the ObjectWriterProvider to use
* @param objectClass the class containing the field
* @param fieldName the name of the field
* @param ordinal the ordinal position of the field
* @param features the features to use
* @param format the date format to use
* @param locale the locale to use
* @param label the label for the field
* @param fieldType the type of the field
* @param fieldClass the class of the field
* @param field the Field to create a writer for
* @param method the Method to create a writer for
* @param function the Function to create a writer for
* @param contentAs the contentAs class
* @return a FieldWriter instance
*/
public <T, V> FieldWriter<T> createFieldWriter(
ObjectWriterProvider provider,
Class<T> objectClass,

View File

@ -23,6 +23,38 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* ObjectWriterProvider is responsible for providing and managing ObjectWriter instances
* for serializing Java objects into JSON format. It handles object writer creation, caching,
* type conversion, and various serialization features.
*
* <p>This provider supports various features including:
* <ul>
* <li>Object writer caching for performance optimization</li>
* <li>Mixin support for modifying serialization behavior</li>
* <li>Module-based extensibility</li>
* <li>Custom type writer registration</li>
* <li>Property naming strategy configuration</li>
* </ul>
*
* <p>Example usage:
* <pre>
* // Get default provider
* ObjectWriterProvider provider = JSONFactory.getDefaultObjectWriterProvider();
*
* // Get object writer for a specific type
* ObjectWriter&lt;User&gt; writer = provider.getObjectWriter(User.class);
*
* // Serialize object to JSON string
* User user = new User(1, "John");
* String jsonString = writer.toJSONString(user);
*
* // Register custom object writer
* provider.register(User.class, new CustomUserWriter());
* </pre>
*
* @since 2.0.0
*/
public class ObjectWriterProvider
implements ObjectCodecProvider {
static final int TYPE_INT32_MASK = 1 << 1;
@ -48,10 +80,18 @@ public class ObjectWriterProvider
volatile long userDefineMask;
boolean alphabetic = JSONFactory.isDefaultWriterAlphabetic();
/**
* Constructs an ObjectWriterProvider with default settings.
*/
public ObjectWriterProvider() {
this((PropertyNamingStrategy) null);
}
/**
* Constructs an ObjectWriterProvider with the specified naming strategy.
*
* @param namingStrategy the property naming strategy to use
*/
public ObjectWriterProvider(PropertyNamingStrategy namingStrategy) {
init();
@ -79,16 +119,29 @@ public class ObjectWriterProvider
this.namingStrategy = namingStrategy;
}
/**
* Constructs an ObjectWriterProvider with the specified ObjectWriterCreator.
*
* @param creator the ObjectWriterCreator to use for creating ObjectWriter instances
*/
public ObjectWriterProvider(ObjectWriterCreator creator) {
init();
this.creator = creator;
}
/**
* Gets the property naming strategy used by this provider.
*
* @return the property naming strategy, or null if none is set
*/
public PropertyNamingStrategy getNamingStrategy() {
return namingStrategy;
}
/**
* Sets whether to use compatible field name behavior.
*
* @param stat true to enable compatible field name behavior, false to disable
* @deprecated only use compatible with fastjson 1.x
*/
public void setCompatibleWithFieldName(boolean stat) {
@ -99,10 +152,23 @@ public class ObjectWriterProvider
}
}
/**
* Sets the property naming strategy used by this provider.
*
* @param namingStrategy the property naming strategy to set
*/
public void setNamingStrategy(PropertyNamingStrategy namingStrategy) {
this.namingStrategy = namingStrategy;
}
/**
* Registers a mixin mapping between a target class and a mixin source class.
* Mixin allows modifying the serialization behavior of a class by applying
* annotations from another class.
*
* @param target the target class to which the mixin will be applied
* @param mixinSource the source class from which annotations will be copied, or null to remove the mixin
*/
public void mixIn(Class target, Class mixinSource) {
if (mixinSource == null) {
mixInCache.remove(target);
@ -112,10 +178,20 @@ public class ObjectWriterProvider
cache.remove(target);
}
/**
* Clears all mixin mappings.
*/
public void cleanupMixIn() {
mixInCache.clear();
}
/**
* Gets the ObjectWriterCreator used by this provider. If a context-specific creator
* is available, it will be returned; otherwise, the default creator for this provider
* will be returned.
*
* @return the ObjectWriterCreator
*/
public ObjectWriterCreator getCreator() {
ObjectWriterCreator contextCreator = JSONFactory.getContextWriterCreator();
if (contextCreator != null) {
@ -124,11 +200,26 @@ public class ObjectWriterProvider
return creator;
}
/**
* Registers an ObjectWriter for the specified type using the default field-based setting.
*
* @param type the type for which to register the ObjectWriter
* @param objectWriter the ObjectWriter to register, or null to unregister
* @return the previous ObjectWriter for the type, or null if there was no previous ObjectWriter
*/
public ObjectWriter register(Type type, ObjectWriter objectWriter) {
boolean fieldBased = (JSONFactory.getDefaultWriterFeatures() & JSONWriter.Feature.FieldBased.mask) != 0;
return register(type, objectWriter, fieldBased);
}
/**
* Registers an ObjectWriter for the specified type.
*
* @param type the type for which to register the ObjectWriter
* @param objectWriter the ObjectWriter to register, or null to unregister
* @param fieldBased whether the ObjectWriter is field-based
* @return the previous ObjectWriter for the type, or null if there was no previous ObjectWriter
*/
public ObjectWriter register(Type type, ObjectWriter objectWriter, boolean fieldBased) {
if (type == Integer.class) {
if (objectWriter == null || objectWriter == ObjectWriterImplInt32.INSTANCE) {
@ -171,33 +262,86 @@ public class ObjectWriterProvider
return cache.put(type, objectWriter);
}
/**
* Registers an ObjectWriter for the specified type using method-based writing
* if it is not already registered.
*
* @param type the type for which to register the ObjectWriter
* @param objectWriter the ObjectWriter to register
* @return the previous ObjectWriter for the type, or null if there was no previous ObjectWriter
*/
public ObjectWriter registerIfAbsent(Type type, ObjectWriter objectWriter) {
return registerIfAbsent(type, objectWriter, false);
}
/**
* Registers an ObjectWriter for the specified type if it is not already registered.
*
* @param type the type for which to register the ObjectWriter
* @param objectWriter the ObjectWriter to register
* @param fieldBased whether the ObjectWriter is field-based
* @return the previous ObjectWriter for the type, or null if there was no previous ObjectWriter
*/
public ObjectWriter registerIfAbsent(Type type, ObjectWriter objectWriter, boolean fieldBased) {
ConcurrentMap<Type, ObjectWriter> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.putIfAbsent(type, objectWriter);
}
/**
* Unregisters the ObjectWriter for the specified type using method-based writing.
*
* @param type the type for which to unregister the ObjectWriter
* @return the unregistered ObjectWriter, or null if there was no ObjectWriter for the type
*/
public ObjectWriter unregister(Type type) {
return unregister(type, false);
}
/**
* Unregisters the ObjectWriter for the specified type.
*
* @param type the type for which to unregister the ObjectWriter
* @param fieldBased whether the ObjectWriter is field-based
* @return the unregistered ObjectWriter, or null if there was no ObjectWriter for the type
*/
public ObjectWriter unregister(Type type, boolean fieldBased) {
ConcurrentMap<Type, ObjectWriter> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.remove(type);
}
/**
* Unregisters the specified ObjectWriter for the given type, but only if the currently
* registered writer matches the specified writer.
*
* @param type the type for which to unregister the ObjectWriter
* @param objectWriter the ObjectWriter to unregister
* @return true if the ObjectWriter was unregistered, false otherwise
*/
public boolean unregister(Type type, ObjectWriter objectWriter) {
return unregister(type, objectWriter, false);
}
/**
* Unregisters the specified ObjectWriter for the given type, but only if the currently
* registered writer matches the specified writer.
*
* @param type the type for which to unregister the ObjectWriter
* @param objectWriter the ObjectWriter to unregister
* @param fieldBased whether the ObjectWriter is field-based
* @return true if the ObjectWriter was unregistered, false otherwise
*/
public boolean unregister(Type type, ObjectWriter objectWriter, boolean fieldBased) {
ConcurrentMap<Type, ObjectWriter> cache = fieldBased ? this.cacheFieldBased : this.cache;
return cache.remove(type, objectWriter);
}
/**
* Registers an ObjectWriterModule. If the module is already registered, this method
* does nothing and returns false.
*
* @param module the module to register
* @return true if the module was registered, false if it was already registered
*/
public boolean register(ObjectWriterModule module) {
for (int i = modules.size() - 1; i >= 0; i--) {
if (modules.get(i) == module) {
@ -211,22 +355,50 @@ public class ObjectWriterProvider
return true;
}
/**
* Unregisters an ObjectWriterModule.
*
* @param module the module to unregister
* @return true if the module was unregistered, false if it was not registered
*/
public boolean unregister(ObjectWriterModule module) {
return modules.remove(module);
}
/**
* Gets the mixin source class for the specified target class.
*
* @param target the target class
* @return the mixin source class, or null if no mixin is registered for the target
*/
public Class getMixIn(Class target) {
return mixInCache.get(target);
}
/**
* Initializes this provider with the base module.
*/
public void init() {
modules.add(new ObjectWriterBaseModule(this));
}
/**
* Gets the list of registered ObjectWriter modules.
*
* @return the list of modules
*/
public List<ObjectWriterModule> getModules() {
return modules;
}
/**
* Gets field information for the specified field of a class.
*
* @param beanInfo the BeanInfo object to populate with bean information
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the field
* @param field the field for which to get information
*/
public void getFieldInfo(BeanInfo beanInfo, FieldInfo fieldInfo, Class objectClass, Field field) {
for (int i = 0; i < modules.size(); i++) {
ObjectWriterModule module = modules.get(i);
@ -238,6 +410,14 @@ public class ObjectWriterProvider
}
}
/**
* Gets field information for the specified method of a class.
*
* @param beanInfo the BeanInfo object to populate with bean information
* @param fieldInfo the FieldInfo object to populate with field information
* @param objectClass the class containing the method
* @param method the method for which to get information
*/
public void getFieldInfo(BeanInfo beanInfo, FieldInfo fieldInfo, Class objectClass, Method method) {
for (int i = 0; i < modules.size(); i++) {
ObjectWriterModule module = modules.get(i);
@ -249,6 +429,12 @@ public class ObjectWriterProvider
}
}
/**
* Gets bean information for the specified class by delegating to registered modules.
*
* @param beanInfo the BeanInfo object to populate with bean information
* @param objectClass the class for which to get bean information
*/
public void getBeanInfo(BeanInfo beanInfo, Class objectClass) {
if (namingStrategy != null && namingStrategy != PropertyNamingStrategy.NeverUseThisValueExceptDefaultValue) {
beanInfo.namingStrategy = namingStrategy.name();
@ -264,6 +450,14 @@ public class ObjectWriterProvider
}
}
/**
* Gets an ObjectWriter for the specified type with formatting.
*
* @param objectType the type for which to get an ObjectWriter
* @param format the format string to use
* @param locale the locale to use
* @return the ObjectWriter for the specified type
*/
public ObjectWriter getObjectWriter(Type objectType, String format, Locale locale) {
if (objectType == Double.class) {
return new ObjectWriterImplDouble(new DecimalFormat(format));
@ -304,25 +498,61 @@ public class ObjectWriterProvider
return getObjectWriter(objectType);
}
/**
* Gets an ObjectWriter for the specified class.
*
* @param objectClass the class for which to get an ObjectWriter
* @return the ObjectWriter for the specified class
*/
public ObjectWriter getObjectWriter(Class objectClass) {
return getObjectWriter(objectClass, objectClass, false);
}
/**
* Gets an ObjectWriter for the specified type and class.
*
* @param objectType the type for which to get an ObjectWriter
* @param objectClass the class for which to get an ObjectWriter
* @return the ObjectWriter for the specified type and class
*/
public ObjectWriter getObjectWriter(Type objectType, Class objectClass) {
return getObjectWriter(objectType, objectClass, false);
}
/**
* Gets an ObjectWriter for the specified type.
*
* @param objectType the type for which to get an ObjectWriter
* @return the ObjectWriter for the specified type
*/
public ObjectWriter getObjectWriter(Type objectType) {
Class objectClass = TypeUtils.getClass(objectType);
return getObjectWriter(objectType, objectClass, false);
}
/**
* Gets an ObjectWriter from the cache for the specified type and class.
*
* @param objectType the type for which to get an ObjectWriter
* @param objectClass the class for which to get an ObjectWriter
* @param fieldBased whether to use field-based writing
* @return the ObjectWriter from the cache, or null if not found
*/
public ObjectWriter getObjectWriterFromCache(Type objectType, Class objectClass, boolean fieldBased) {
return fieldBased
? cacheFieldBased.get(objectType)
: cache.get(objectType);
}
/**
* Gets an ObjectWriter for the specified type, class, and format with field-based option.
*
* @param objectType the type for which to get an ObjectWriter
* @param objectClass the class for which to get an ObjectWriter
* @param format the format string to use
* @param fieldBased whether to use field-based writing
* @return the ObjectWriter for the specified type, class, and format
*/
public ObjectWriter getObjectWriter(Type objectType, Class objectClass, String format, boolean fieldBased) {
ObjectWriter objectWriter = getObjectWriter(objectType, objectClass, fieldBased);
if (format != null) {
@ -333,6 +563,16 @@ public class ObjectWriterProvider
return objectWriter;
}
/**
* Gets an ObjectWriter for the specified type, class, and field-based option.
* If an ObjectWriter for the type is already cached, it will be returned directly.
* Otherwise, a new ObjectWriter will be created and cached.
*
* @param objectType the type for which to get an ObjectWriter
* @param objectClass the class for which to get an ObjectWriter
* @param fieldBased whether to use field-based writing
* @return the ObjectWriter for the specified type and class
*/
public ObjectWriter getObjectWriter(Type objectType, Class objectClass, boolean fieldBased) {
ObjectWriter objectWriter = fieldBased
? cacheFieldBased.get(objectType)
@ -551,17 +791,31 @@ public class ObjectWriterProvider
NOT_REFERENCES_TYPE_HASH_CODES = codes2;
}
/**
* Checks if the specified class is a primitive type or an enum.
*
* @param clazz the class to check
* @return true if the class is a primitive type or an enum, false otherwise
*/
public static boolean isPrimitiveOrEnum(final Class<?> clazz) {
return Arrays.binarySearch(PRIMITIVE_HASH_CODES, System.identityHashCode(clazz)) >= 0
|| ((clazz.getModifiers() & ENUM) != 0 && clazz.getSuperclass() == Enum.class);
}
/**
* Checks if the specified class is a type that should not have reference detection.
*
* @param clazz the class to check
* @return true if the class should not have reference detection, false otherwise
*/
public static boolean isNotReferenceDetect(final Class<?> clazz) {
return Arrays.binarySearch(NOT_REFERENCES_TYPE_HASH_CODES, System.identityHashCode(clazz)) >= 0
|| ((clazz.getModifiers() & ENUM) != 0 && clazz.getSuperclass() == Enum.class);
}
/**
* Clears all cached ObjectWriters and mixin mappings.
*
* @since 2.0.53
*/
public void clear() {
@ -570,6 +824,11 @@ public class ObjectWriterProvider
cacheFieldBased.clear();
}
/**
* Cleans up cached ObjectWriters and mixin mappings associated with the specified class.
*
* @param objectClass the class for which to clean up cached ObjectWriters
*/
public void cleanup(Class objectClass) {
mixInCache.remove(objectClass);
cache.remove(objectClass);
@ -619,6 +878,13 @@ public class ObjectWriterProvider
return false;
}
/**
* Cleans up cached ObjectWriters associated with the specified ClassLoader.
* This method removes all cached writers that are related to classes loaded
* by the given ClassLoader.
*
* @param classLoader the ClassLoader for which to clean up cached ObjectWriters
*/
public void cleanup(ClassLoader classLoader) {
mixInCache.entrySet().removeIf
(entry -> entry.getKey().getClassLoader() == classLoader
@ -637,50 +903,110 @@ public class ObjectWriterProvider
BeanUtils.cleanupCache(classLoader);
}
/**
* Checks if reference detection is disabled.
*
* @return true if reference detection is disabled, false otherwise
*/
public boolean isDisableReferenceDetect() {
return disableReferenceDetect;
}
/**
* Checks if auto-type support is disabled.
*
* @return true if auto-type support is disabled, false otherwise
*/
public boolean isDisableAutoType() {
return disableAutoType;
}
/**
* Checks if JSONB support is disabled.
*
* @return true if JSONB support is disabled, false otherwise
*/
public boolean isDisableJSONB() {
return disableJSONB;
}
/**
* Checks if array mapping is disabled.
*
* @return true if array mapping is disabled, false otherwise
*/
public boolean isDisableArrayMapping() {
return disableArrayMapping;
}
/**
* Sets whether reference detection is disabled.
*
* @param disableReferenceDetect true to disable reference detection, false to enable it
*/
public void setDisableReferenceDetect(boolean disableReferenceDetect) {
this.disableReferenceDetect = disableReferenceDetect;
}
/**
* Sets whether array mapping is disabled.
*
* @param disableArrayMapping true to disable array mapping, false to enable it
*/
public void setDisableArrayMapping(boolean disableArrayMapping) {
this.disableArrayMapping = disableArrayMapping;
}
/**
* Sets whether JSONB support is disabled.
*
* @param disableJSONB true to disable JSONB support, false to enable it
*/
public void setDisableJSONB(boolean disableJSONB) {
this.disableJSONB = disableJSONB;
}
/**
* Sets whether auto-type support is disabled.
*
* @param disableAutoType true to disable auto-type support, false to enable it
*/
public void setDisableAutoType(boolean disableAutoType) {
this.disableAutoType = disableAutoType;
}
/**
* Checks if alphabetic ordering is enabled.
*
* @return true if alphabetic ordering is enabled, false otherwise
*/
public boolean isAlphabetic() {
return alphabetic;
}
/**
* Checks if transient fields should be skipped.
*
* @return true if transient fields should be skipped, false otherwise
*/
public boolean isSkipTransient() {
return skipTransient;
}
/**
* Sets whether transient fields should be skipped.
*
* @param skipTransient true to skip transient fields, false to include them
*/
public void setSkipTransient(boolean skipTransient) {
this.skipTransient = skipTransient;
}
/**
* Creates a new BeanInfo instance.
*
* @return a new BeanInfo instance
*/
protected BeanInfo createBeanInfo() {
return new BeanInfo(this);
}