Skip to content

Commit bc90ee4

Browse files
authored
Merge pull request #1095 from Lyin-OTTF/fix/gh-1076-fromjson-binding-javadoc
Update Javadocs for fromJson()
2 parents 9926542 + 88b6be9 commit bc90ee4

3 files changed

Lines changed: 79 additions & 6 deletions

File tree

‎src/main/java/org/json/JSONObject.java‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,12 +3472,15 @@ private Type[] getMapTypes(Type type) {
34723472
* of the given class. It supports basic data types including int, double, float,
34733473
* long, and boolean (as well as their boxed counterparts). The class must have a
34743474
* no-argument constructor, and the field names in the class must match the keys
3475-
* in the JSON string.
3475+
* in the JSON string. Static fields are ignored; see {@link #fromJson(Class)} for
3476+
* how the remaining fields are bound.
34763477
*
34773478
* @param <T> the type of the object to return
34783479
* @param jsonString json in string format
34793480
* @param clazz the class of the object to be returned
34803481
* @return an instance of Object T with fields populated from the JSON string
3482+
* @throws JSONException if jsonString is not valid JSON, or if it cannot be bound to clazz
3483+
* as described in {@link #fromJson(Class)}
34813484
*/
34823485
public static <T> T fromJson(String jsonString, Class<T> clazz) {
34833486
JSONObject jsonObject = new JSONObject(jsonString);
@@ -3493,15 +3496,20 @@ public static <T> T fromJson(String jsonString, Class<T> clazz) {
34933496
* The target class must have a no-argument constructor, and its field names must match
34943497
* the keys in the JSON string. Static fields are ignored.
34953498
*
3496-
* <p><strong>Note:</strong> Only classes that are explicitly supported and registered within
3497-
* the {@code JSONObject} context can be deserialized. If the provided class is not among those,
3498-
* this method will not be able to deserialize it. This ensures that only a limited and
3499-
* controlled set of types can be instantiated from JSON for safety and predictability.
3499+
* <p><strong>Note:</strong> binding is driven by the keys present in the JSON, not by
3500+
* how the fields are declared. Every non-static field declared by the class is written
3501+
* when its name matches a key, including {@code private} and {@code transient} fields,
3502+
* and field access is widened with
3503+
* {@link java.lang.reflect.Field#setAccessible(boolean)}. {@link JSONPropertyIgnore}
3504+
* only suppresses the bean to JSONObject direction, so it does not keep a field from
3505+
* being populated here. JSON coming from an untrusted source should therefore be
3506+
* validated, or reduced to the expected keys, before it is handed to this method.
35003507
*
35013508
* @param clazz the class of the object to be returned
35023509
* @param <T> the type of the object
35033510
* @return an instance of type {@code T} with fields populated from the JSON string
3504-
* @throws IllegalArgumentException if the class is not supported for deserialization
3511+
* @throws JSONException if the class has no accessible no-argument constructor, or if a
3512+
* field cannot be instantiated or set
35053513
*/
35063514
@SuppressWarnings("unchecked")
35073515
public <T> T fromJson(Class<T> clazz) {

‎src/test/java/org/json/junit/JSONObjectTest.java‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.json.junit.data.CustomClassH;
6868
import org.json.junit.data.CustomClassI;
6969
import org.json.junit.data.CustomClassJ;
70+
import org.json.junit.data.FromJsonKeyBindingExample;
7071
import org.junit.After;
7172
import org.junit.Ignore;
7273
import org.junit.Test;
@@ -3883,6 +3884,31 @@ public void jsonObjectParseFromJson_9() {
38833884
}
38843885
}
38853886

3887+
/**
3888+
* Pins down the field binding done by {@code fromJson(Class)}: every non-static
3889+
* declared field whose name matches a JSON key is written, no matter how the
3890+
* field is declared. Private fields, transient fields and fields hidden behind
3891+
* a {@code @JSONPropertyIgnore} getter are all populated, because that
3892+
* annotation only affects the bean to JSONObject direction. See issue #1076.
3893+
*/
3894+
@Test
3895+
public void jsonObjectParseFromJson_10() {
3896+
JSONObject object = new JSONObject()
3897+
.put("displayName", "bob")
3898+
.put("privateBoolean", true)
3899+
.put("privateString", "private-value")
3900+
.put("transientString", "transient-value")
3901+
.put("ignoredGetterBoolean", true);
3902+
3903+
FromJsonKeyBindingExample bound = object.fromJson(FromJsonKeyBindingExample.class);
3904+
3905+
assertEquals("private field should be populated", true, bound.isPrivateBoolean());
3906+
assertEquals("private field should be populated", "private-value", bound.getPrivateString());
3907+
assertEquals("transient field should be populated", "transient-value", bound.getTransientString());
3908+
assertEquals("field behind a @JSONPropertyIgnore getter should be populated", true,
3909+
bound.isIgnoredGetterBoolean());
3910+
}
3911+
38863912
@Test
38873913
public void testMaxNumberLength() {
38883914
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.json.junit.data;
2+
3+
import org.json.JSONPropertyIgnore;
4+
5+
/**
6+
* Bean used to pin down how {@code JSONObject#fromJson(Class)} binds JSON keys
7+
* to fields: {@code privateString} and {@code privateBoolean} are private,
8+
* {@code transientString} is transient, and {@code ignoredGetterBoolean} is
9+
* hidden behind a getter annotated with {@code @JSONPropertyIgnore} (which only
10+
* suppresses the bean to JSONObject direction).
11+
*/
12+
public class FromJsonKeyBindingExample {
13+
public String displayName;
14+
private boolean privateBoolean;
15+
private String privateString;
16+
private transient String transientString;
17+
private boolean ignoredGetterBoolean;
18+
19+
public FromJsonKeyBindingExample() {
20+
// Required for JSONObject#fromJson(Class<T>) tests.
21+
}
22+
23+
public boolean isPrivateBoolean() {
24+
return privateBoolean;
25+
}
26+
27+
public String getPrivateString() {
28+
return privateString;
29+
}
30+
31+
public String getTransientString() {
32+
return transientString;
33+
}
34+
35+
@JSONPropertyIgnore
36+
public boolean isIgnoredGetterBoolean() {
37+
return ignoredGetterBoolean;
38+
}
39+
}

0 commit comments

Comments
 (0)