TongchengOpenSource / smart-doc

Smart-doc is a java restful api document generation tool. Smart-doc is based on interface source code analysis to generate interface documentation, completely zero-injection.
https://smart-doc-group.github.io/#/
Apache License 2.0
1.43k stars 278 forks source link

Request parameters for wrapper classes are not currently supported, although I added generics #899

Closed jay763190097 closed 3 months ago

jay763190097 commented 3 months ago

Contact Details

jiangmang.1992@163.com

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

shalousun commented 3 months ago

@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.

jay763190097 commented 3 months ago

@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能正常看到结构,证明不是外部源代码的问题。

shalousun commented 3 months ago

@jay763190097 Fields modified by the transient keyword are ignored by smart-doc.

jay763190097 commented 3 months ago

@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.

shalousun commented 3 months ago

@jay763190097 Let's first test different JSON serialization scenarios, and then make a fix based on the actual results.

jay763190097 commented 3 months ago

@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 commented 3 months ago

@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.

linwumingshi commented 3 months ago

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:

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

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.


shalousun commented 3 months ago

@jay763190097 For versions after 3.0.8, you can configure the serialization of fields marked with transients: reference configuration options:

{
  "serializeRequestTransients": true,
  "serializeResponseTransients": false
}
jay763190097 commented 2 months ago

@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?

jay763190097 commented 2 months ago

image @shalousun @linwumingshi Thanks all, I checked the PR, you already considered the old config property, Cool !!!, I'll use the property serializeRequestTransients.