diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java
index 257eb1074..4e8b42c97 100644
--- a/src/main/java/org/json/JSONObject.java
+++ b/src/main/java/org/json/JSONObject.java
@@ -17,6 +17,9 @@
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.GenericArrayType;
/**
* A JSONObject is an unordered collection of name/value pairs. Its external
@@ -3207,4 +3210,250 @@ private static JSONException recursivelyDefinedObjectException(String key) {
"JavaBean object contains recursively defined member variable of key " + quote(key)
);
}
+
+ /**
+ * Helper method to extract the raw Class from Type.
+ */
+ private Class> getRawType(Type type) {
+ if (type instanceof Class) {
+ return (Class>) type;
+ } else if (type instanceof ParameterizedType) {
+ return (Class>) ((ParameterizedType) type).getRawType();
+ } else if (type instanceof GenericArrayType) {
+ return Object[].class; // Simplified handling for arrays
+ }
+ return Object.class; // Fallback
+ }
+
+ /**
+ * Extracts the element Type for a Collection Type.
+ */
+ private Type getElementType(Type type) {
+ if (type instanceof ParameterizedType) {
+ Type[] args = ((ParameterizedType) type).getActualTypeArguments();
+ return args.length > 0 ? args[0] : Object.class;
+ }
+ return Object.class;
+ }
+
+ /**
+ * Extracts the key and value Types for a Map Type.
+ */
+ private Type[] getMapTypes(Type type) {
+ if (type instanceof ParameterizedType) {
+ Type[] args = ((ParameterizedType) type).getActualTypeArguments();
+ if (args.length == 2) {
+ return args;
+ }
+ }
+ return new Type[]{Object.class, Object.class}; // Default: String keys, Object values
+ }
+
+ /**
+ * Deserializes a JSON string into an instance of the specified class.
+ *
+ *
This method attempts to map JSON key-value pairs to the corresponding fields
+ * of the given class. It supports basic data types including int, double, float,
+ * long, and boolean (as well as their boxed counterparts). The class must have a
+ * no-argument constructor, and the field names in the class must match the keys
+ * in the JSON string.
+ *
+ * @param jsonString json in string format
+ * @param clazz the class of the object to be returned
+ * @return an instance of Object T with fields populated from the JSON string
+ */
+ public static T fromJson(String jsonString, Class clazz) {
+ JSONObject jsonObject = new JSONObject(jsonString);
+ return jsonObject.fromJson(clazz);
+ }
+
+ /**
+ * Deserializes a JSON string into an instance of the specified class.
+ *
+ *
This method attempts to map JSON key-value pairs to the corresponding fields
+ * of the given class. It supports basic data types including {@code int}, {@code double},
+ * {@code float}, {@code long}, and {@code boolean}, as well as their boxed counterparts.
+ * The target class must have a no-argument constructor, and its field names must match
+ * the keys in the JSON string.
+ *
+ *
Note: Only classes that are explicitly supported and registered within
+ * the {@code JSONObject} context can be deserialized. If the provided class is not among those,
+ * this method will not be able to deserialize it. This ensures that only a limited and
+ * controlled set of types can be instantiated from JSON for safety and predictability.
+ *
+ * @param clazz the class of the object to be returned
+ * @param the type of the object
+ * @return an instance of type {@code T} with fields populated from the JSON string
+ * @throws IllegalArgumentException if the class is not supported for deserialization
+ */
+ @SuppressWarnings("unchecked")
+ public T fromJson(Class clazz) {
+ try {
+ T obj = clazz.getDeclaredConstructor().newInstance();
+ for (Field field : clazz.getDeclaredFields()) {
+ field.setAccessible(true);
+ String fieldName = field.getName();
+ if (has(fieldName)) {
+ Object value = get(fieldName);
+ Type fieldType = field.getGenericType();
+ Object convertedValue = convertValue(value, fieldType);
+ field.set(obj, convertedValue);
+ }
+ }
+ return obj;
+ } catch (NoSuchMethodException e) {
+ throw new JSONException("No no-arg constructor for class: " + clazz.getName(), e);
+ } catch (Exception e) {
+ throw new JSONException("Failed to instantiate or set field for class: " + clazz.getName(), e);
+ }
+ }
+
+ /**
+ * Recursively converts a value to the target Type, handling nested generics for Collections and Maps.
+ */
+ private Object convertValue(Object value, Type targetType) throws JSONException {
+ if (value == null) {
+ return null;
+ }
+
+ Class> rawType = getRawType(targetType);
+
+ // Direct assignment
+ if (rawType.isAssignableFrom(value.getClass())) {
+ return value;
+ }
+
+ if (rawType == int.class || rawType == Integer.class) {
+ return ((Number) value).intValue();
+ } else if (rawType == double.class || rawType == Double.class) {
+ return ((Number) value).doubleValue();
+ } else if (rawType == float.class || rawType == Float.class) {
+ return ((Number) value).floatValue();
+ } else if (rawType == long.class || rawType == Long.class) {
+ return ((Number) value).longValue();
+ } else if (rawType == boolean.class || rawType == Boolean.class) {
+ return value;
+ } else if (rawType == String.class) {
+ return value;
+ } else if (rawType == BigDecimal.class) {
+ return new BigDecimal((String) value);
+ } else if (rawType == BigInteger.class) {
+ return new BigInteger((String) value);
+ }
+
+ // Enum conversion
+ if (rawType.isEnum() && value instanceof String) {
+ return stringToEnum(rawType, (String) value);
+ }
+
+ // Collection handling (e.g., List>>)
+ if (Collection.class.isAssignableFrom(rawType)) {
+ if (value instanceof JSONArray) {
+ Type elementType = getElementType(targetType);
+ return fromJsonArray((JSONArray) value, rawType, elementType);
+ }
+ }
+ // Map handling (e.g., Map>)
+ else if (Map.class.isAssignableFrom(rawType) && value instanceof JSONObject) {
+ Type[] mapTypes = getMapTypes(targetType);
+ Type keyType = mapTypes[0];
+ Type valueType = mapTypes[1];
+ return convertToMap((JSONObject) value, keyType, valueType, rawType);
+ }
+ // POJO handling (including custom classes like Tuple)
+ else if (!rawType.isPrimitive() && !rawType.isEnum() && value instanceof JSONObject) {
+ // Recurse with the raw class for POJO deserialization
+ return ((JSONObject) value).fromJson(rawType);
+ }
+
+ // Fallback
+ return value.toString();
+ }
+
+ /**
+ * Converts a JSONObject to a Map with the specified generic key and value Types.
+ * Supports nested types via recursive convertValue.
+ */
+ private Map, ?> convertToMap(JSONObject jsonMap, Type keyType, Type valueType, Class> mapType) throws JSONException {
+ try {
+ @SuppressWarnings("unchecked")
+ Map