1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.xml.http.converter;
18
19 import static java.util.Objects.isNull;
20
21 import java.util.Set;
22 import jakarta.xml.bind.JAXBElement;
23 import jakarta.xml.bind.JAXBException;
24 import jakarta.xml.bind.MarshalException;
25 import jakarta.xml.bind.Marshaller;
26 import jakarta.xml.bind.PropertyException;
27 import jakarta.xml.bind.UnmarshalException;
28 import jakarta.xml.bind.Unmarshaller;
29 import jakarta.xml.bind.annotation.XmlRootElement;
30 import javax.xml.transform.Result;
31 import javax.xml.transform.Source;
32 import org.bremersee.xml.JaxbContextBuilder;
33 import org.springframework.http.HttpHeaders;
34 import org.springframework.http.MediaType;
35 import org.springframework.http.converter.HttpMessageConversionException;
36 import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter;
37 import org.springframework.lang.NonNull;
38 import org.springframework.lang.Nullable;
39 import org.springframework.util.Assert;
40
41
42
43
44
45
46
47 public class Jaxb2HttpMessageConverter extends AbstractXmlHttpMessageConverter<Object> {
48
49 private final JaxbContextBuilder jaxbContextBuilder;
50
51 private final Set<Class<?>> ignoreReadingClasses;
52
53 private final Set<Class<?>> ignoreWritingClasses;
54
55
56
57
58
59
60 public Jaxb2HttpMessageConverter(JaxbContextBuilder jaxbContextBuilder) {
61 this(jaxbContextBuilder, null, null);
62 }
63
64
65
66
67
68
69
70
71 public Jaxb2HttpMessageConverter(
72 JaxbContextBuilder jaxbContextBuilder,
73 Set<Class<?>> ignoreReadingClasses,
74 Set<Class<?>> ignoreWritingClasses) {
75 Assert.notNull(jaxbContextBuilder, "JaxbContextBuilder must be present.");
76 this.jaxbContextBuilder = jaxbContextBuilder;
77 this.ignoreReadingClasses = isNull(ignoreReadingClasses) ? Set.of() : ignoreReadingClasses;
78 this.ignoreWritingClasses = isNull(ignoreWritingClasses) ? Set.of() : ignoreWritingClasses;
79 }
80
81 @Override
82 public boolean canRead(@NonNull Class<?> clazz, @Nullable MediaType mediaType) {
83 return !ignoreReadingClasses.contains(clazz)
84 && jaxbContextBuilder.canUnmarshal(clazz)
85 && this.canRead(mediaType);
86 }
87
88 @Override
89 public boolean canWrite(@NonNull Class<?> clazz, @Nullable MediaType mediaType) {
90 return !ignoreWritingClasses.contains(clazz)
91 && jaxbContextBuilder.canMarshal(clazz)
92 && this.canWrite(mediaType);
93 }
94
95 @Override
96 protected boolean supports(@NonNull Class<?> clazz) {
97 throw new UnsupportedOperationException();
98 }
99
100 @NonNull
101 @Override
102 protected Object readFromSource(
103 @NonNull Class<?> clazz,
104 @NonNull HttpHeaders headers,
105 @NonNull Source source) throws Exception {
106
107 try {
108
109 Unmarshaller unmarshaller = jaxbContextBuilder.buildUnmarshaller(clazz);
110 if (clazz.isAnnotationPresent(XmlRootElement.class)) {
111 return unmarshaller.unmarshal(source);
112 } else {
113 JAXBElement<?> jaxbElement = unmarshaller.unmarshal(source, clazz);
114 return jaxbElement.getValue();
115 }
116 } catch (UnmarshalException unmarshalException) {
117 throw unmarshalException;
118 } catch (JAXBException jaxbException) {
119 throw new HttpMessageConversionException("Invalid JAXB setup: "
120 + jaxbException.getMessage(), jaxbException);
121 }
122 }
123
124 @Override
125 protected void writeToResult(
126 @NonNull Object o,
127 HttpHeaders headers,
128 @NonNull Result result) throws Exception {
129
130 try {
131 Marshaller marshaller = jaxbContextBuilder.buildMarshaller(o);
132 this.setCharset(headers.getContentType(), marshaller);
133 marshaller.marshal(o, result);
134 } catch (MarshalException marshalException) {
135 throw marshalException;
136 } catch (JAXBException jaxbException) {
137 throw new HttpMessageConversionException("Invalid JAXB setup: "
138 + jaxbException.getMessage(), jaxbException);
139 }
140 }
141
142 private void setCharset(
143 @Nullable MediaType contentType,
144 Marshaller marshaller) throws PropertyException {
145
146 if (contentType != null && contentType.getCharset() != null) {
147 marshaller.setProperty("jaxb.encoding", contentType.getCharset().name());
148 }
149 }
150
151 }