Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.

Commit 8ed7b32

Browse files
authored
Merge pull request #924 from linwumingshi/fix/map-enum-key-primitive-value-json-example
fix: 🐛 Fix empty JSON example value for Map with enum keys and primitive values
2 parents 6ea5e09 + 94bbe7d commit 8ed7b32

14 files changed

Lines changed: 1139 additions & 740 deletions

src/main/java/com/ly/doc/constants/DocAnnotationConstants.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ public interface DocAnnotationConstants {
9797
*/
9898
String VALUE_PROP = "value";
9999

100+
/**
101+
* annotation using prop
102+
*/
103+
String USING_PROP = "using";
104+
100105
/**
101106
* annotation path prop
102107
*/

src/main/java/com/ly/doc/constants/DocGlobalConstants.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,4 +614,16 @@ public interface DocGlobalConstants {
614614
*/
615615
String DEFAULT_MAP_KEY_DESC = "A map key.";
616616

617+
/**
618+
* object map value warning.
619+
*/
620+
String OBJECT_MAP_VALUE_WARNING = "{\"warning\":\"Using java.util.Object as a Map value is not recommended. "
621+
+ "Smart-doc cannot process it properly. Please use a specific type for better documentation generation.\"}";
622+
623+
/**
624+
* object list warning.
625+
*/
626+
String GENERIC_LIST_WARNING = "{\"warning\":\"Using java.util.Object in a List instead of a specific generic type is not recommended. "
627+
+ "Smart-doc cannot display the correct generics. Please specify the generic type for better documentation generation.\"}";
628+
617629
}

src/main/java/com/ly/doc/extension/json/PropertyNamingStrategies.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@
2020
*/
2121
package com.ly.doc.extension.json;
2222

23+
// @formatter:off
2324
/**
2425
* copy from jackson and singleton instances.
25-
*
26-
* @see <a href=
27-
* "https://github.com/FasterXML/jackson-databind/blob/2.18/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java">PropertyNamingStrategies</a>.
26+
* <p>
27+
* <a href="https://github.com/FasterXML/jackson-databind/blob/2.18/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java">PropertyNamingStrategies</a>.
2828
* @author xingzi
2929
*/
30+
// @formatter:on
3031
public abstract class PropertyNamingStrategies implements java.io.Serializable {
3132

3233
/**

src/main/java/com/ly/doc/helper/BaseHelper.java

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,27 @@
2020
*/
2121
package com.ly.doc.helper;
2222

23+
import com.ly.doc.builder.ProjectDocConfigBuilder;
24+
import com.ly.doc.constants.DocAnnotationConstants;
2325
import com.ly.doc.constants.DocTags;
26+
import com.ly.doc.constants.JSRAnnotationConstants;
27+
import com.ly.doc.model.CustomField;
28+
import com.ly.doc.model.CustomFieldInfo;
29+
import com.ly.doc.model.DocJavaField;
30+
import com.ly.doc.model.FieldJsonAnnotationInfo;
2431
import com.ly.doc.utils.DocUtil;
32+
import com.ly.doc.utils.JavaClassUtil;
2533
import com.ly.doc.utils.JavaClassValidateUtil;
34+
import com.power.common.util.CollectionUtil;
2635
import com.power.common.util.StringEscapeUtil;
2736
import com.power.common.util.StringUtil;
37+
import com.thoughtworks.qdox.model.JavaAnnotation;
38+
import com.thoughtworks.qdox.model.JavaField;
39+
import com.thoughtworks.qdox.model.expression.AnnotationValue;
2840

2941
import java.util.Map;
42+
import java.util.Objects;
43+
import java.util.Set;
3044

3145
/**
3246
* Abstract Base helper
@@ -70,4 +84,207 @@ protected static String getFieldValueFromMock(Map<String, String> tagsMap) {
7084
return fieldValue;
7185
}
7286

87+
/**
88+
* check custom field is ignored
89+
* @param docField doc field
90+
* @param isResp is resp
91+
* @param customRequestField custom request field
92+
* @param customResponseField custom response field
93+
* @return boolean
94+
*/
95+
protected static boolean isIgnoreCustomField(DocJavaField docField, boolean isResp, CustomField customRequestField,
96+
CustomField customResponseField) {
97+
if (Objects.nonNull(customRequestField) && JavaClassUtil.isTargetChildClass(docField.getDeclaringClassName(),
98+
customRequestField.getOwnerClassName()) && (customRequestField.isIgnore()) && !isResp) {
99+
return true;
100+
}
101+
return Objects.nonNull(customResponseField) && JavaClassUtil
102+
.isTargetChildClass(docField.getDeclaringClassName(), customResponseField.getOwnerClassName())
103+
&& (customResponseField.isIgnore()) && isResp;
104+
}
105+
106+
/**
107+
* check field is transient
108+
* @param field field
109+
* @param projectBuilder project builder
110+
* @param isResp is resp
111+
* @return boolean
112+
*/
113+
protected static boolean isTransientField(JavaField field, ProjectDocConfigBuilder projectBuilder, boolean isResp) {
114+
if (field.isTransient()) {
115+
return (projectBuilder.getApiConfig().isSerializeRequestTransients() && !isResp)
116+
|| (projectBuilder.getApiConfig().isSerializeResponseTransients() && isResp);
117+
}
118+
return false;
119+
}
120+
121+
/**
122+
* Get a field JSON annotation information for a given field.
123+
* @param projectBuilder the project builder
124+
* @param docField the doc of java field
125+
* @param isResp the response flag for the parameter
126+
* @param groupClasses the group classes
127+
* @param methodJsonViewClasses the method JSON view classes
128+
* @return the field JSON annotation information {@link FieldJsonAnnotationInfo}
129+
*
130+
*/
131+
protected static FieldJsonAnnotationInfo getFieldJsonAnnotationInfo(ProjectDocConfigBuilder projectBuilder,
132+
DocJavaField docField, boolean isResp, Set<String> groupClasses, Set<String> methodJsonViewClasses) {
133+
FieldJsonAnnotationInfo fieldJsonAnnotationInfo = new FieldJsonAnnotationInfo();
134+
// Handle @JsonView; if the field is not annotated with @JsonView, skip
135+
if (!methodJsonViewClasses.isEmpty() && isResp && docField.getAnnotations().isEmpty()) {
136+
return fieldJsonAnnotationInfo;
137+
}
138+
139+
for (JavaAnnotation annotation : docField.getAnnotations()) {
140+
// if the field is annotated with @JsonIgnore || @JsonProperty, then
141+
// check if it belongs to the groupClasses
142+
if (JavaClassValidateUtil.isIgnoreFieldJson(annotation, isResp)) {
143+
fieldJsonAnnotationInfo.setIgnore(true);
144+
continue;
145+
}
146+
// Handle @JsonView
147+
if (JavaClassUtil.shouldExcludeFieldFromJsonView(annotation, methodJsonViewClasses, isResp,
148+
projectBuilder)) {
149+
fieldJsonAnnotationInfo.setIgnore(true);
150+
return fieldJsonAnnotationInfo;
151+
}
152+
153+
String annotationName = annotation.getType().getValue();
154+
// if the field is annotated with @JsonSerialize
155+
if (DocAnnotationConstants.SHORT_JSON_SERIALIZE.equals(annotationName)
156+
&& DocAnnotationConstants.TO_STRING_SERIALIZER_USING
157+
.equals(annotation.getNamedParameter(DocAnnotationConstants.USING_PROP))) {
158+
fieldJsonAnnotationInfo.setToStringSerializer(true);
159+
continue;
160+
}
161+
// if the field is annotated with @Null And isResp is false
162+
if (JSRAnnotationConstants.NULL.equals(annotationName) && !isResp) {
163+
if (CollectionUtil.isEmpty(groupClasses)) {
164+
fieldJsonAnnotationInfo.setIgnore(true);
165+
return fieldJsonAnnotationInfo;
166+
}
167+
Set<String> groupClassList = JavaClassUtil.getParamGroupJavaClass(annotation);
168+
for (String javaClass : groupClassList) {
169+
if (groupClasses.contains(javaClass)) {
170+
fieldJsonAnnotationInfo.setIgnore(true);
171+
return fieldJsonAnnotationInfo;
172+
}
173+
}
174+
}
175+
176+
// Handle @JSONField
177+
if (DocAnnotationConstants.SHORT_JSON_FIELD.equals(annotationName)) {
178+
if (null != annotation.getProperty(DocAnnotationConstants.NAME_PROP)) {
179+
fieldJsonAnnotationInfo.setFieldName(StringUtil
180+
.removeQuotes(annotation.getProperty(DocAnnotationConstants.NAME_PROP).toString()));
181+
}
182+
}
183+
184+
// Handle @JsonProperty
185+
else if (DocAnnotationConstants.SHORT_JSON_PROPERTY.equals(annotationName)
186+
|| DocAnnotationConstants.GSON_ALIAS_NAME.equals(annotationName)) {
187+
AnnotationValue annotationValue = annotation.getProperty(DocAnnotationConstants.VALUE_PROP);
188+
if (null != annotationValue) {
189+
fieldJsonAnnotationInfo.setFieldName(StringUtil.removeQuotes(annotationValue.toString()));
190+
}
191+
}
192+
// Handle JSR303 required
193+
if (JavaClassValidateUtil.isJSR303Required(annotationName) && !isResp) {
194+
Set<String> groupClassList = JavaClassUtil.getParamGroupJavaClass(annotation);
195+
// Check if groupClasses contains any element from
196+
// groupClassList
197+
boolean hasGroup = groupClassList.stream().anyMatch(groupClasses::contains);
198+
199+
if (hasGroup) {
200+
fieldJsonAnnotationInfo.setStrRequired(true);
201+
}
202+
else if (CollectionUtil.isEmpty(groupClasses)) {
203+
// If the annotation is @Valid or @Validated, the Default
204+
// group is added by default and groupClasses will not be
205+
// empty;
206+
// In other cases, if groupClasses is still empty, then
207+
// strRequired is false.
208+
fieldJsonAnnotationInfo.setStrRequired(false);
209+
}
210+
}
211+
// Handle @JsonFormat
212+
if (DocAnnotationConstants.JSON_FORMAT.equals(annotationName)) {
213+
fieldJsonAnnotationInfo.setFieldJsonFormatType(
214+
DocUtil.processFieldTypeNameByJsonFormat(projectBuilder.getApiConfig().getShowJavaType(),
215+
docField.getTypeFullyQualifiedName(), annotation));
216+
fieldJsonAnnotationInfo
217+
.setFieldJsonFormatValue(DocUtil.getJsonFormatString(docField.getJavaField(), annotation));
218+
}
219+
220+
}
221+
return fieldJsonAnnotationInfo;
222+
}
223+
224+
/**
225+
* Get the custom field information for a given field.
226+
* @param projectBuilder the project builder
227+
* @param docField the doc of java field
228+
* @param customResponseField the custom response field
229+
* @param customRequestField the custom request field
230+
* @param isResp the response flag for the parameter
231+
* @param simpleName the simple name of the field
232+
* @return the custom field information {@link CustomFieldInfo}
233+
*
234+
*/
235+
protected static CustomFieldInfo getCustomFieldInfo(ProjectDocConfigBuilder projectBuilder, DocJavaField docField,
236+
CustomField customResponseField, CustomField customRequestField, boolean isResp, String simpleName) {
237+
CustomFieldInfo customFieldInfo = new CustomFieldInfo();
238+
239+
// ignore custom field, if true return quickly
240+
if (isIgnoreCustomField(docField, isResp, customRequestField, customResponseField)) {
241+
customFieldInfo.setIgnore(true);
242+
return customFieldInfo;
243+
}
244+
245+
// cover response value
246+
if (Objects.nonNull(customResponseField) && isResp && Objects.nonNull(customResponseField.getValue())
247+
&& JavaClassUtil.isTargetChildClass(simpleName, customResponseField.getOwnerClassName())) {
248+
249+
customFieldInfo.setFieldValue(String.valueOf(customResponseField.getValue()));
250+
}
251+
252+
// cover request value
253+
if (Objects.nonNull(customRequestField) && !isResp && Objects.nonNull(customRequestField.getValue())
254+
&& JavaClassUtil.isTargetChildClass(simpleName, customRequestField.getOwnerClassName())) {
255+
256+
customFieldInfo.setFieldValue(String.valueOf(customRequestField.getValue()));
257+
}
258+
259+
// cover required
260+
if (Objects.nonNull(customRequestField) && !isResp
261+
&& JavaClassUtil.isTargetChildClass(simpleName, customRequestField.getOwnerClassName())
262+
&& customRequestField.isRequire()) {
263+
264+
customFieldInfo.setStrRequired(true);
265+
}
266+
267+
// cover comment
268+
if (Objects.nonNull(customRequestField) && StringUtil.isNotEmpty(customRequestField.getDesc())
269+
&& JavaClassUtil.isTargetChildClass(simpleName, customRequestField.getOwnerClassName()) && !isResp) {
270+
customFieldInfo.setComment(customRequestField.getDesc());
271+
}
272+
if (Objects.nonNull(customResponseField) && StringUtil.isNotEmpty(customResponseField.getDesc())
273+
&& JavaClassUtil.isTargetChildClass(simpleName, customResponseField.getOwnerClassName()) && isResp) {
274+
customFieldInfo.setComment(customResponseField.getDesc());
275+
}
276+
277+
// cover fieldName
278+
if (Objects.nonNull(customRequestField) && StringUtil.isNotEmpty(customRequestField.getReplaceName())
279+
&& JavaClassUtil.isTargetChildClass(simpleName, customRequestField.getOwnerClassName()) && !isResp) {
280+
customFieldInfo.setFieldName(customRequestField.getReplaceName());
281+
}
282+
if (Objects.nonNull(customResponseField) && StringUtil.isNotEmpty(customResponseField.getReplaceName())
283+
&& JavaClassUtil.isTargetChildClass(simpleName, customResponseField.getOwnerClassName()) && isResp) {
284+
285+
customFieldInfo.setFieldName(customResponseField.getReplaceName());
286+
}
287+
return customFieldInfo;
288+
}
289+
73290
}

0 commit comments

Comments
 (0)