mirror of
https://github.com/stleary/JSON-java.git
synced 2026-01-05 00:00:52 -05:00
Fix: Support Java record accessors in JSONObject
This commit is contained in:
@@ -1885,7 +1885,8 @@ public class JSONObject {
|
||||
}
|
||||
|
||||
private static boolean isValidMethodName(String name) {
|
||||
return !"getClass".equals(name) && !"getDeclaringClass".equals(name);
|
||||
return !"getClass".equals(name)
|
||||
&& !"getDeclaringClass".equals(name);
|
||||
}
|
||||
|
||||
private static String getKeyNameFromMethod(Method method) {
|
||||
@@ -1909,6 +1910,32 @@ public class JSONObject {
|
||||
} else if (name.startsWith("is") && name.length() > 2) {
|
||||
key = name.substring(2);
|
||||
} else {
|
||||
// Check if this is a record-style accessor (no prefix)
|
||||
// Record accessors are simple method names that match field names
|
||||
// They must start with a lowercase letter and should be declared in the class itself
|
||||
// (not inherited from Object, Enum, Number, or any java.* class)
|
||||
// Also exclude common Object/bean method names
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
if (name.length() > 0 && Character.isLowerCase(name.charAt(0))
|
||||
&& !"get".equals(name)
|
||||
&& !"is".equals(name)
|
||||
&& !"set".equals(name)
|
||||
&& !"toString".equals(name)
|
||||
&& !"hashCode".equals(name)
|
||||
&& !"equals".equals(name)
|
||||
&& !"clone".equals(name)
|
||||
&& !"notify".equals(name)
|
||||
&& !"notifyAll".equals(name)
|
||||
&& !"wait".equals(name)
|
||||
&& declaringClass != null
|
||||
&& declaringClass != Object.class
|
||||
&& !Enum.class.isAssignableFrom(declaringClass)
|
||||
&& !Number.class.isAssignableFrom(declaringClass)
|
||||
&& !declaringClass.getName().startsWith("java.")
|
||||
&& !declaringClass.getName().startsWith("javax.")) {
|
||||
// This is a record-style accessor - return the method name as-is
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// if the first letter in the key is not uppercase, then skip.
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.json.junit.data.MyJsonString;
|
||||
import org.json.junit.data.MyNumber;
|
||||
import org.json.junit.data.MyNumberContainer;
|
||||
import org.json.junit.data.MyPublicClass;
|
||||
import org.json.junit.data.PersonRecord;
|
||||
import org.json.junit.data.RecursiveBean;
|
||||
import org.json.junit.data.RecursiveBeanEquals;
|
||||
import org.json.junit.data.Singleton;
|
||||
@@ -796,6 +797,25 @@ public class JSONObjectTest {
|
||||
Util.checkJSONObjectMaps(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONObject built from a Java record.
|
||||
* Records use accessor methods without get/is prefixes (e.g., name() instead of getName()).
|
||||
* This test verifies that JSONObject correctly handles record types.
|
||||
*/
|
||||
@Test
|
||||
public void jsonObjectByRecord() {
|
||||
PersonRecord person = new PersonRecord("John Doe", 30, true);
|
||||
JSONObject jsonObject = new JSONObject(person);
|
||||
|
||||
// validate JSON
|
||||
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
|
||||
assertTrue("expected 3 top level items", ((Map<?,?>)(JsonPath.read(doc, "$"))).size() == 3);
|
||||
assertTrue("expected name field", "John Doe".equals(jsonObject.query("/name")));
|
||||
assertTrue("expected age field", Integer.valueOf(30).equals(jsonObject.query("/age")));
|
||||
assertTrue("expected active field", Boolean.TRUE.equals(jsonObject.query("/active")));
|
||||
Util.checkJSONObjectMaps(jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* A bean is also an object. But in order to test the JSONObject
|
||||
* ctor that takes an object and a list of names,
|
||||
|
||||
31
src/test/java/org/json/junit/data/PersonRecord.java
Normal file
31
src/test/java/org/json/junit/data/PersonRecord.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package org.json.junit.data;
|
||||
|
||||
/**
|
||||
* A test class that mimics Java record accessor patterns.
|
||||
* Records use accessor methods without get/is prefixes (e.g., name() instead of getName()).
|
||||
* This class simulates that behavior to test JSONObject's handling of such methods.
|
||||
*/
|
||||
public class PersonRecord {
|
||||
private final String name;
|
||||
private final int age;
|
||||
private final boolean active;
|
||||
|
||||
public PersonRecord(String name, int age, boolean active) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
// Record-style accessors (no "get" or "is" prefix)
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int age() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public boolean active() {
|
||||
return active;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user