Request parameters for wrapper classes are not currently supported, although I added generics
Closed this issue · 11 comments
Contact Details
Version
3.0.7
Plugin
smart-doc-maven-plugin
Build tool version
maven 3.6.2
Jdk version
17
Describe the bug (Bug描述,社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
My request object is CommonRequest, which has two properties, header and body, Where the body is a generic wrapper object. I used generics on the requested api interface, specifying the type of body. But there is no structure on the document to generate the body.
Expected Behavior (您期望的结果,社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
Expected to support wrapper classes as request parameters and return results
Current Behavior (当前结果,社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
Structure has only header, no body
Steps to Reproduce (Bug产生步骤,请尽量提供用例代码。社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
With this wrapper class,
you can reproduce the problem.
Possible Solution (Bug解决建议,社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
No response
Context (Bug影响描述,社区已开启国际化推广,请用文心一言、讯飞星火等辅助翻译成英文,减少社区开发者的工作)
No response
Validations
- Check if you're on the latest smart-doc version. (请检查是否为最新版本)
- Read the docs. (请先阅读官方文档)
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. (检查是否存在报告相同错误的问题,以避免产生重复)
@jay763190097 Can you provide a test case? The code sample should include a Controller interface and CommonRequest. You can include the test code in the issue.
@shalousun 就是文档里面的参数对象,其实现在用的很多,为了统一结构。
CommonRequest来自一个lib包
public class CommonRequest<T> {
private RequesterHeader header;
private transient T body;
}
/**
* customer creation api
* @param request com.abc.CustomerCreateDto
* @return
*
*/
@PostMapping("/customer/create")
public CommonResult<CustomerCreateResDto> create(@RequestBody @Valid CommonRequest<CustomerCreateDto> request) {
}
文档值生成了CommonRequest里面的header结构,body完全没有。。。加不加param都是这样的。
header能正常看到结构,证明不是外部源代码的问题。
@jay763190097 Fields modified by the transient keyword are ignored by smart-doc.
@shalousun The transient keyword, which is used for deserialization in the request body, makes sense, Because it will no longer be serialized. The idea is that even if it is serialized, it reduces the overhead of serialization, so it should be parsed. If it is in the response body, we should ignore it because this is a serialization operation.
@jay763190097 Let's first test different JSON serialization scenarios, and then make a fix based on the actual results.
@shalousun for serialization scenario, we use transient to ignore this field, that‘s right.
But for de-serialization scenario, we should parse this filed.
@shalousun for serialization scenario, we use transient to ignore this field, that‘s right. But for de-serialization scenario, we should parse this filed.
Through testing, it has been found that the handling of fields modified with transient
varies across different JSON processing frameworks. In Jackson, such fields are not ignored by default, whereas in Fastjson, they are ignored. As a result, the community has had to provide a separate configuration option to determine whether fields marked with transient
should be included or not.
Additional Explanation: Handling of transient
Fields by Different JSON Serialization Frameworks
Different JSON serialization frameworks handle transient
fields in various ways. The following code demonstrates these differences:
Example Class Demo
package com.power.doc.model;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
@Data
public class TestTransient implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* name
*/
private String name;
/**
* password
*/
private transient String password;
@Override
public String toString() {
return "TestTransient{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
Example Junit Test Class
package com.power.doc.utils;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.power.doc.model.TestTransient;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
* json transient test
*/
public class JsonTransientTest {
/**
* Test Json transient filed
*/
@DisplayName("Test Json transient filed")
@Test
public void test() throws JsonProcessingException {
TestTransient testTransient = new TestTransient();
testTransient.setName("test");
testTransient.setPassword("123456");
ObjectMapper objectMapper = new ObjectMapper();
System.out.println("com.alibaba.fastjson.JSON:" + com.alibaba.fastjson.JSON.toJSON(testTransient));
System.out.println("com.alibaba.fastjson2.JSON:" + JSON.toJSON(testTransient));
System.out.println("com.fasterxml.jackson.databind.ObjectMapper:" + objectMapper.writeValueAsString(testTransient));
System.out.println("com.google.gson.Gson:" + new Gson().toJson(testTransient));
// com.alibaba.fastjson.JSON:{"password":"123456","name":"test"}
// com.alibaba.fastjson2.JSON:{"name":"test"}
// com.fasterxml.jackson.databind.ObjectMapper:{"name":"test","password":"123456"}
// com.google.gson.Gson:{"name":"test"}
System.out.println();
String test = "{\"password\":\"123456\",\"name\":\"test\"}";
System.out.println("com.alibaba.fastjson.JSON:" + com.alibaba.fastjson.JSON.parseObject(test, TestTransient.class));
System.out.println("com.alibaba.fastjson2.JSON:" + JSON.parseObject(test, TestTransient.class));
System.out.println("com.fasterxml.jackson.databind.ObjectMapper:" + objectMapper.readValue(test, TestTransient.class));
System.out.println("com.google.gson.Gson:" + new Gson().fromJson(test, TestTransient.class));
// com.alibaba.fastjson.JSON:TestTransient{name='test', password='123456'}
// com.alibaba.fastjson2.JSON:TestTransient{name='test', password='123456'}
// com.fasterxml.jackson.databind.ObjectMapper:TestTransient{name='test', password='123456'}
// com.google.gson.Gson:TestTransient{name='test', password='null'}
}
}
Analysis of Results:
-
Serialization:
com.alibaba.fastjson.JSON
andcom.fasterxml.jackson.databind.ObjectMapper
include thetransient
field during serialization, producing{"name":"test","password":"123456"}
.com.alibaba.fastjson2.JSON
andcom.google.gson.Gson
exclude thetransient
field during serialization, resulting in{"name":"test"}
.
-
Deserialization:
com.alibaba.fastjson.JSON
,com.alibaba.fastjson2.JSON
, andcom.fasterxml.jackson.databind.ObjectMapper
successfully deserialize thetransient
field, producingTestTransient{name='test', password='123456'}
.com.google.gson.Gson
does not deserialize thetransient
field, resulting inTestTransient{name='test', password='null'}
.
Conclusion:
The handling of transient
fields varies among different JSON frameworks. Below is a summary table that illustrates these differences:
Framework | Serialization (Includes transient field) |
Deserialization (Restores transient field) |
---|---|---|
com.alibaba.fastjson.JSON | Yes | Yes |
com.alibaba.fastjson2.JSON | No | Yes |
com.fasterxml.jackson.databind.ObjectMapper | Yes | Yes |
com.google.gson.Gson | No | No |
- Serialization: Whether the framework includes the
transient
field during JSON serialization. - Deserialization: Whether the framework restores the value of the
transient
field during JSON deserialization.
This table highlights that Fastjson
(original version) and Jackson
include transient
fields in both serialization and deserialization. Fastjson2
excludes the transient
field during serialization but correctly restores it during deserialization. Gson
, on the other hand, excludes the transient
field in both serialization and deserialization processes.
@jay763190097 For versions after 3.0.8, you can configure the serialization of fields marked with transients
: reference configuration options:
{
"serializeRequestTransients": true,
"serializeResponseTransients": false
}
@shalousun @linwumingshi After checking the source code, I found a config property skipTransientField, the default value is true. I added this property and changed it to false, then It worked for me, didn't sync with you guys, my bad. Is it a duplicate property you are defined?
@shalousun @linwumingshi Thanks all, I checked the PR, you already considered the old config property, Cool !!!, I'll use the property serializeRequestTransients.