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) {
|
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) {
|
private static String getKeyNameFromMethod(Method method) {
|
||||||
@@ -1909,6 +1910,32 @@ public class JSONObject {
|
|||||||
} else if (name.startsWith("is") && name.length() > 2) {
|
} else if (name.startsWith("is") && name.length() > 2) {
|
||||||
key = name.substring(2);
|
key = name.substring(2);
|
||||||
} else {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
// if the first letter in the key is not uppercase, then skip.
|
// 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.MyNumber;
|
||||||
import org.json.junit.data.MyNumberContainer;
|
import org.json.junit.data.MyNumberContainer;
|
||||||
import org.json.junit.data.MyPublicClass;
|
import org.json.junit.data.MyPublicClass;
|
||||||
|
import org.json.junit.data.PersonRecord;
|
||||||
import org.json.junit.data.RecursiveBean;
|
import org.json.junit.data.RecursiveBean;
|
||||||
import org.json.junit.data.RecursiveBeanEquals;
|
import org.json.junit.data.RecursiveBeanEquals;
|
||||||
import org.json.junit.data.Singleton;
|
import org.json.junit.data.Singleton;
|
||||||
@@ -796,6 +797,25 @@ public class JSONObjectTest {
|
|||||||
Util.checkJSONObjectMaps(jsonObject);
|
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
|
* A bean is also an object. But in order to test the JSONObject
|
||||||
* ctor that takes an object and a list of names,
|
* 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