[BUG] JSONObject.parseObject 方法设置 JSONReader.Feature.IgnoreSetNullValue 特性时不生效,以及 JSONObject.parseObject(String, Class<T>) 和 JSONObject.parseObject(String).to(Class<T>) 逻辑不一致的问题
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
1. `JSONObject.parseObject` 方法反序列化对象时,设置 `JSONReader.Feature.IgnoreSetNullValue` 特性时不生效;
2. 由此引出的 `JSONObject.parseObject(String, Class)` 与 `JSONObject.parseObject(String).to(Class)` 逻辑不一致。
### 环境信息
- OS信息: Windows 10 Professional Intel(R) Core(TM) i5-9400F @ 2.90GHz
- JDK信息: java 18.0.1 2022-04-19
- 版本信息:Fastjson2 2.0.42
### 重现步骤
testcase:
```java
package com.doghole.mess.test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
public class IgnoreSetNullValueTest {
static class TestClass {
private Integer field1 = 1;
private Integer field2;
private Integer field3 = 3;
public void setField1(Integer field1) {
if (field1 == null) throw new NullPointerException();
this.field1 = field1;
}
public void setField2(Integer field2) {
if (field2 == null) throw new NullPointerException();
this.field2 = field2;
}
public void setField3(Integer field3) {
this.field3 = field3;
}
public Integer getField1() {
return field1;
}
public Integer getField2() {
return field2;
}
public Integer getField3() {
return field3;
}
}
@Test
void test() {
// Assertion 1
// Deserialize json string which not contains null field
// Passed.
Assertions.assertDoesNotThrow(new Executable() {
@Override
public void execute() throws Throwable {
JSONObject.parseObject("{\"field1\": -1}", TestClass.class);
JSONObject.parseObject("{\"field2\": 2}", TestClass.class);
JSONObject.parseObject("{}", TestClass.class);
}
});
// Assertion 2
// Deserialize json string which contains null field without JSONReader.Feature.IgnoreSetNullValue
// Passed, because it called the `setField2` setter and threw an NPE
Assertions.assertThrows(Exception.class, new Executable() {
@Override
public void execute() throws Throwable {
JSONObject.parseObject("{\"field1\": -1, \"field2\": null}", TestClass.class);
}
});
// Assertion 3
// Deserialize json string which contains null field without JSONReader.Feature.IgnoreSetNullValue,
// but using `to` method.
// Failed. It didn't call the `setField2` setter, so there's no any experted NPE presented.
// Why? I didn't set neither IgnoreSetNullValue feature nor IgnoreNullPropertyValue feature,
// shouldn't the `JSONObject.parseObject(String, Class)` shares the same logic with `JSONObject.parseObject(String).to(Class)`?
Assertions.assertThrows(Exception.class, new Executable() {
@Override
public void execute() throws Throwable {
// Addictionally, field1 is set to -1 successfully, but seems it automatically ignores field2
// without appointed any ignored feature.
JSONObject.parseObject("{\"field1\": -1, \"field2\": null}").to(TestClass.class);
}
});
// Assertion 4
// And go back to my original purpose, deserialize json string which contains null field with JSONReader.Feature.IgnoreSetNullValue
// Failed, JSONReader.Feature.IgnoreSetNullValue didn't work.
Assertions.assertDoesNotThrow(new Executable() {
@Override
public void execute() throws Throwable {
JSONObject.parseObject("{\"field1\": null}", TestClass.class, JSONReader.Feature.IgnoreSetNullValue);
}
});
// Assertion 5
// Also failed, JSONReader.Feature.IgnoreNullPropertyValue didn't work.
// But maybe I've confused the difference between `IgnoreNullPropertyValue` and `IgnoreSetNullValue`
Assertions.assertDoesNotThrow(new Executable() {
@Override
public void execute() throws Throwable {
JSONObject.parseObject("{\"field1\": null}", TestClass.class, JSONReader.Feature.IgnoreNullPropertyValue);
}
});
// Go further, try setting a null value to a default-init not null field: field3.
// Neither IgnoreSetNullValue feature nor IgnoreNullPropertyValue feature is set.
TestClass testOverrideDefaultValue = JSONObject.parseObject("{\"field1\": -1,\"field3\": null}").to(TestClass.class);
// Assertion 6. Passed.
Assertions.assertEquals(-1, testOverrideDefaultValue.getField1());
// Assertion 7. Failed, also because the `setField3` setter is not called. It's acturally a same issue with assertion 3
Assertions.assertTrue(null == testOverrideDefaultValue.getField3());
// As comparing, using `JSONObject.parseObject(String, Class)` method:
TestClass testOverrideDefaultValue2 = JSONObject.parseObject("{\"field1\": -1,\"field3\": null}", TestClass.class);
// Assertion 8. Passed.
Assertions.assertEquals(-1, testOverrideDefaultValue2.getField1());
// Assertion 9. Passed.
Assertions.assertTrue(null == testOverrideDefaultValue2.getField3());
}
}
```
### 期待的正确结果
JUnit 测试通过
#### 附加信息
无
Contributor guide
Research direction
Start by running the supplied IgnoreSetNullValueTest reproduction, then trace the JSONObject.parseObject(..., Class, JSONReader.Feature) and JSONObject.parseObject(...).to(Class) entry points. Done means the feature behavior is honored and both deserialization paths produce the expected setter and null-value behavior, with the JUnit assertions passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100