Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 41adbd3

Browse files
authored
Merge pull request springfox#3051 from brunomendola/feature/2734/jackson-xml
jackson-dataformat-xml plugin fixes springfox#2734
2 parents 4d4577b + c761cfe commit 41adbd3

8 files changed

Lines changed: 450 additions & 22 deletions

File tree

springfox-schema/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ext {
1212
dependencies {
1313
compile project(':springfox-core')
1414
compile project(':springfox-spi')
15+
provided "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${jackson}"
1516
testCompile libs.test
1617
testCompile project(':springfox-core').sourceSets.test.output
1718
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
*
3+
* Copyright 2017-2019 the original author or authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*
18+
*/
19+
package springfox.documentation.schema;
20+
21+
import org.springframework.context.annotation.Condition;
22+
import org.springframework.context.annotation.ConditionContext;
23+
import org.springframework.core.type.AnnotatedTypeMetadata;
24+
import org.springframework.util.ClassUtils;
25+
26+
import static org.springframework.util.ClassUtils.forName;
27+
28+
public abstract class ClassPresentInClassPathCondition implements Condition {
29+
@Override
30+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
31+
return isPresent(getClassName(), context.getClassLoader());
32+
}
33+
34+
protected abstract String getClassName();
35+
36+
private static boolean isPresent(String className, ClassLoader classLoader) {
37+
if (classLoader == null) {
38+
classLoader = ClassUtils.getDefaultClassLoader();
39+
}
40+
try {
41+
forName(className, classLoader);
42+
return true;
43+
} catch (Throwable ex) {
44+
return false;
45+
}
46+
}
47+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package springfox.documentation.schema;
2+
3+
public class JacksonXmlPresentInClassPathCondition extends ClassPresentInClassPathCondition {
4+
@Override
5+
protected String getClassName() {
6+
return "com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty";
7+
}
8+
}

springfox-schema/src/main/java/springfox/documentation/schema/JaxbPresentInClassPathCondition.java

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,9 @@
1818
*/
1919
package springfox.documentation.schema;
2020

21-
import org.springframework.context.annotation.Condition;
22-
import org.springframework.context.annotation.ConditionContext;
23-
import org.springframework.core.type.AnnotatedTypeMetadata;
24-
import org.springframework.util.ClassUtils;
25-
26-
import static org.springframework.util.ClassUtils.*;
27-
28-
public class JaxbPresentInClassPathCondition implements Condition {
21+
public class JaxbPresentInClassPathCondition extends ClassPresentInClassPathCondition {
2922
@Override
30-
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
31-
return isPresent("javax.xml.bind.annotation.XmlElement", context.getClassLoader());
32-
}
33-
34-
private static boolean isPresent(String className, ClassLoader classLoader) {
35-
if (classLoader == null) {
36-
classLoader = ClassUtils.getDefaultClassLoader();
37-
}
38-
try {
39-
forName(className, classLoader);
40-
return true;
41-
} catch (Throwable ex) {
42-
return false;
43-
}
23+
protected String getClassName() {
24+
return "javax.xml.bind.annotation.XmlElement";
4425
}
4526
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
*
3+
* Copyright 2017-2019 the original author or authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*
18+
*/
19+
package springfox.documentation.schema.plugins;
20+
21+
import com.fasterxml.classmate.TypeResolver;
22+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Conditional;
25+
import org.springframework.core.annotation.AnnotationUtils;
26+
import org.springframework.stereotype.Component;
27+
import springfox.documentation.schema.JacksonXmlPresentInClassPathCondition;
28+
import springfox.documentation.schema.Xml;
29+
import springfox.documentation.spi.DocumentationType;
30+
import springfox.documentation.spi.schema.ModelBuilderPlugin;
31+
import springfox.documentation.spi.schema.contexts.ModelContext;
32+
33+
@Component
34+
@Conditional(JacksonXmlPresentInClassPathCondition.class)
35+
public class JacksonXmlModelPlugin implements ModelBuilderPlugin {
36+
private final TypeResolver typeResolver;
37+
38+
@Autowired
39+
public JacksonXmlModelPlugin(TypeResolver typeResolver) {
40+
this.typeResolver = typeResolver;
41+
}
42+
43+
@Override
44+
public void apply(ModelContext context) {
45+
JacksonXmlRootElement root = AnnotationUtils.findAnnotation(forClass(context), JacksonXmlRootElement.class);
46+
if (root != null) {
47+
context.getBuilder().xml(buildXml(root));
48+
}
49+
}
50+
51+
private Xml buildXml(JacksonXmlRootElement annotation) {
52+
return new Xml()
53+
.name(defaultToNull(annotation.localName()))
54+
.attribute(false)
55+
.namespace(defaultToNull(annotation.namespace()))
56+
.wrapped(false);
57+
}
58+
59+
private String defaultToNull(String value) {
60+
return "##default".equalsIgnoreCase(value) ? null : value;
61+
}
62+
63+
private Class<?> forClass(ModelContext context) {
64+
return typeResolver.resolve(context.getType()).getErasedType();
65+
}
66+
67+
@Override
68+
public boolean supports(DocumentationType delimiter) {
69+
return true;
70+
}
71+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
*
3+
* Copyright 2017-2019 the original author or authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*
18+
*/
19+
package springfox.documentation.schema.property;
20+
21+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
22+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
23+
import org.springframework.context.annotation.Conditional;
24+
import org.springframework.core.annotation.AnnotationUtils;
25+
import org.springframework.stereotype.Component;
26+
import springfox.documentation.schema.JacksonXmlPresentInClassPathCondition;
27+
import springfox.documentation.schema.Xml;
28+
import springfox.documentation.spi.DocumentationType;
29+
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
30+
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
31+
32+
import java.lang.annotation.Annotation;
33+
import java.lang.reflect.AnnotatedElement;
34+
import java.util.Optional;
35+
import java.util.function.Predicate;
36+
37+
import static java.util.Optional.empty;
38+
import static java.util.Optional.ofNullable;
39+
import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
40+
41+
@Component
42+
@Conditional(JacksonXmlPresentInClassPathCondition.class)
43+
public class JacksonXmlPropertyPlugin implements ModelPropertyBuilderPlugin {
44+
45+
@Override
46+
public void apply(ModelPropertyContext context) {
47+
Optional<JacksonXmlProperty> propertyAnnotation = findAnnotation(context, JacksonXmlProperty.class);
48+
49+
if (propertyAnnotation.isPresent()) {
50+
if (propertyAnnotation.get().isAttribute()) {
51+
context.getBuilder()
52+
.xml(new Xml()
53+
.attribute(true)
54+
.namespace(defaultToNull(propertyAnnotation.get().namespace()))
55+
.name(propertyName(propertyAnnotation))
56+
.wrapped(false));
57+
} else {
58+
Optional<JacksonXmlElementWrapper> wrapper = findAnnotation(context, JacksonXmlElementWrapper.class);
59+
context.getBuilder()
60+
.xml(new Xml()
61+
.attribute(false)
62+
.namespace(defaultToNull(propertyAnnotation.get().namespace()))
63+
.name(wrapperName(wrapper, propertyAnnotation))
64+
.wrapped(wrapper.isPresent()));
65+
}
66+
}
67+
}
68+
69+
private static <T extends Annotation> Optional<T> findAnnotation(
70+
ModelPropertyContext context,
71+
Class<T> annotationClass) {
72+
Optional<T> annotation = empty();
73+
if (context.getAnnotatedElement().isPresent()) {
74+
annotation = annotation.map(Optional::of).orElse(findAnnotation(
75+
context.getAnnotatedElement().get(),
76+
annotationClass));
77+
}
78+
if (context.getBeanPropertyDefinition().isPresent()) {
79+
annotation = annotation.map(Optional::of).orElse(findPropertyAnnotation(
80+
context.getBeanPropertyDefinition().get(),
81+
annotationClass));
82+
}
83+
return annotation;
84+
}
85+
86+
public static <T extends Annotation> Optional<T> findAnnotation(
87+
AnnotatedElement annotated,
88+
Class<T> annotation) {
89+
return ofNullable(AnnotationUtils.getAnnotation(annotated, annotation));
90+
}
91+
92+
private String wrapperName(Optional<JacksonXmlElementWrapper> wrapper, Optional<JacksonXmlProperty> property) {
93+
if (wrapper.isPresent() && wrapper.get().useWrapping()) {
94+
return ofNullable(defaultToNull(ofNullable(wrapper.get().localName())
95+
.filter(((Predicate<String>) String::isEmpty).negate()).orElse(null)))
96+
.orElse(ofNullable(propertyName(property))
97+
.orElse(null));
98+
}
99+
return propertyName(property);
100+
}
101+
102+
private String propertyName(Optional<JacksonXmlProperty> property) {
103+
if (property.isPresent()) {
104+
return defaultToNull(ofNullable(property.get().localName())
105+
.filter(((Predicate<String>) String::isEmpty).negate())
106+
.orElse(null));
107+
}
108+
return null;
109+
}
110+
111+
private String defaultToNull(String value) {
112+
return "##default".equalsIgnoreCase(value) ? null : value;
113+
}
114+
115+
@Override
116+
public boolean supports(DocumentationType delimiter) {
117+
return true;
118+
}
119+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package springfox.documentation.schema.plugins
2+
3+
import com.fasterxml.classmate.TypeResolver
4+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
5+
import spock.lang.Specification
6+
import spock.lang.Unroll
7+
import springfox.documentation.schema.DefaultGenericTypeNamingStrategy
8+
import springfox.documentation.spi.DocumentationType
9+
import springfox.documentation.spi.schema.AlternateTypeProvider
10+
import springfox.documentation.spi.schema.contexts.ModelContext
11+
12+
import static java.util.Collections.emptySet
13+
14+
class JacksonXmlModelPluginSpec extends Specification {
15+
def "Should support all swagger documentation types"() {
16+
given:
17+
def sut = new JacksonXmlModelPlugin()
18+
19+
expect:
20+
sut.supports(DocumentationType.SPRING_WEB)
21+
sut.supports(DocumentationType.SWAGGER_12)
22+
sut.supports(DocumentationType.SWAGGER_2)
23+
}
24+
25+
@Unroll
26+
def "Xml model plugin parses #type.localName annotation as expected"() {
27+
given:
28+
def resolver = new TypeResolver()
29+
JacksonXmlModelPlugin sut = new JacksonXmlModelPlugin(resolver)
30+
ModelContext context = ModelContext.inputParam(
31+
"0_0",
32+
"group",
33+
resolver.resolve(type),
34+
Optional.empty(),
35+
new HashSet<>(),
36+
DocumentationType.SWAGGER_12,
37+
new AlternateTypeProvider([]),
38+
new DefaultGenericTypeNamingStrategy(),
39+
emptySet())
40+
when:
41+
sut.apply(context)
42+
43+
then:
44+
context.builder.build()?.xml?.name == expected
45+
46+
where:
47+
type | expected
48+
XmlNotAnnotated | null
49+
XmlRootElementAnnotated | "root"
50+
}
51+
52+
class XmlNotAnnotated {
53+
}
54+
55+
@JacksonXmlRootElement(localName = "root")
56+
class XmlRootElementAnnotated {
57+
}
58+
}

0 commit comments

Comments
 (0)