1
1
/*
2
- * Copyright 2002-2022 the original author or authors.
2
+ * Copyright 2002-2023 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
21
21
import java .util .Iterator ;
22
22
import java .util .List ;
23
23
import java .util .Map ;
24
- import java .util .function . BiConsumer ;
24
+ import java .util .Set ;
25
25
import java .util .function .Function ;
26
26
27
- import javax .xml .XMLConstants ;
28
27
import javax .xml .namespace .QName ;
29
28
import javax .xml .stream .XMLEventReader ;
30
29
import javax .xml .stream .XMLInputFactory ;
36
35
import jakarta .xml .bind .UnmarshalException ;
37
36
import jakarta .xml .bind .Unmarshaller ;
38
37
import jakarta .xml .bind .annotation .XmlRootElement ;
39
- import jakarta .xml .bind .annotation .XmlSchema ;
38
+ import jakarta .xml .bind .annotation .XmlSeeAlso ;
40
39
import jakarta .xml .bind .annotation .XmlType ;
41
40
import org .reactivestreams .Publisher ;
42
41
import reactor .core .Exceptions ;
43
42
import reactor .core .publisher .Flux ;
44
43
import reactor .core .publisher .Mono ;
45
- import reactor .core .publisher .SynchronousSink ;
46
44
47
45
import org .springframework .core .ResolvableType ;
48
46
import org .springframework .core .codec .AbstractDecoder ;
54
52
import org .springframework .core .io .buffer .DataBufferUtils ;
55
53
import org .springframework .core .log .LogFormatUtils ;
56
54
import org .springframework .http .MediaType ;
55
+ import org .springframework .lang .NonNull ;
57
56
import org .springframework .lang .Nullable ;
58
- import org .springframework .util .Assert ;
59
- import org .springframework .util .ClassUtils ;
60
57
import org .springframework .util .MimeType ;
61
58
import org .springframework .util .MimeTypeUtils ;
62
59
import org .springframework .util .xml .StaxUtils ;
72
69
*/
73
70
public class Jaxb2XmlDecoder extends AbstractDecoder <Object > {
74
71
75
- /**
76
- * The default value for JAXB annotations.
77
- * @see XmlRootElement#name()
78
- * @see XmlRootElement#namespace()
79
- * @see XmlType#name()
80
- * @see XmlType#namespace()
81
- */
82
- private static final String JAXB_DEFAULT_ANNOTATION_VALUE = "##default" ;
83
-
84
72
private static final XMLInputFactory inputFactory = StaxUtils .createDefensiveInputFactory ();
85
73
86
74
@@ -162,8 +150,8 @@ public Flux<Object> decode(Publisher<DataBuffer> inputStream, ResolvableType ele
162
150
inputStream , ResolvableType .forClass (XMLEvent .class ), mimeType , hints );
163
151
164
152
Class <?> outputClass = elementType .toClass ();
165
- QName typeName = toQName (outputClass );
166
- Flux <List <XMLEvent >> splitEvents = split (xmlEventFlux , typeName );
153
+ Set < QName > typeNames = Jaxb2Helper . toQNames (outputClass );
154
+ Flux <List <XMLEvent >> splitEvents = Jaxb2Helper . split (xmlEventFlux , typeNames );
167
155
168
156
return splitEvents .map (events -> {
169
157
Object value = unmarshal (events , outputClass );
@@ -184,6 +172,7 @@ public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType ele
184
172
}
185
173
186
174
@ Override
175
+ @ NonNull
187
176
public Object decode (DataBuffer dataBuffer , ResolvableType targetType ,
188
177
@ Nullable MimeType mimeType , @ Nullable Map <String , Object > hints ) throws DecodingException {
189
178
@@ -229,7 +218,8 @@ private Object unmarshal(List<XMLEvent> events, Class<?> outputClass) {
229
218
try {
230
219
Unmarshaller unmarshaller = initUnmarshaller (outputClass );
231
220
XMLEventReader eventReader = StaxUtils .createXMLEventReader (events );
232
- if (outputClass .isAnnotationPresent (XmlRootElement .class )) {
221
+ if (outputClass .isAnnotationPresent (XmlRootElement .class ) ||
222
+ outputClass .isAnnotationPresent (XmlSeeAlso .class )) {
233
223
return unmarshaller .unmarshal (eventReader );
234
224
}
235
225
else {
@@ -250,113 +240,4 @@ private Unmarshaller initUnmarshaller(Class<?> outputClass) throws CodecExceptio
250
240
return this .unmarshallerProcessor .apply (unmarshaller );
251
241
}
252
242
253
- /**
254
- * Returns the qualified name for the given class, according to the mapping rules
255
- * in the JAXB specification.
256
- */
257
- QName toQName (Class <?> outputClass ) {
258
- String localPart ;
259
- String namespaceUri ;
260
-
261
- if (outputClass .isAnnotationPresent (XmlRootElement .class )) {
262
- XmlRootElement annotation = outputClass .getAnnotation (XmlRootElement .class );
263
- localPart = annotation .name ();
264
- namespaceUri = annotation .namespace ();
265
- }
266
- else if (outputClass .isAnnotationPresent (XmlType .class )) {
267
- XmlType annotation = outputClass .getAnnotation (XmlType .class );
268
- localPart = annotation .name ();
269
- namespaceUri = annotation .namespace ();
270
- }
271
- else {
272
- throw new IllegalArgumentException ("Output class [" + outputClass .getName () +
273
- "] is neither annotated with @XmlRootElement nor @XmlType" );
274
- }
275
-
276
- if (JAXB_DEFAULT_ANNOTATION_VALUE .equals (localPart )) {
277
- localPart = ClassUtils .getShortNameAsProperty (outputClass );
278
- }
279
- if (JAXB_DEFAULT_ANNOTATION_VALUE .equals (namespaceUri )) {
280
- Package outputClassPackage = outputClass .getPackage ();
281
- if (outputClassPackage != null && outputClassPackage .isAnnotationPresent (XmlSchema .class )) {
282
- XmlSchema annotation = outputClassPackage .getAnnotation (XmlSchema .class );
283
- namespaceUri = annotation .namespace ();
284
- }
285
- else {
286
- namespaceUri = XMLConstants .NULL_NS_URI ;
287
- }
288
- }
289
- return new QName (namespaceUri , localPart );
290
- }
291
-
292
- /**
293
- * Split a flux of {@link XMLEvent XMLEvents} into a flux of XMLEvent lists, one list
294
- * for each branch of the tree that starts with the given qualified name.
295
- * That is, given the XMLEvents shown {@linkplain XmlEventDecoder here},
296
- * and the {@code desiredName} "{@code child}", this method returns a flux
297
- * of two lists, each of which containing the events of a particular branch
298
- * of the tree that starts with "{@code child}".
299
- * <ol>
300
- * <li>The first list, dealing with the first branch of the tree:
301
- * <ol>
302
- * <li>{@link javax.xml.stream.events.StartElement} {@code child}</li>
303
- * <li>{@link javax.xml.stream.events.Characters} {@code foo}</li>
304
- * <li>{@link javax.xml.stream.events.EndElement} {@code child}</li>
305
- * </ol>
306
- * <li>The second list, dealing with the second branch of the tree:
307
- * <ol>
308
- * <li>{@link javax.xml.stream.events.StartElement} {@code child}</li>
309
- * <li>{@link javax.xml.stream.events.Characters} {@code bar}</li>
310
- * <li>{@link javax.xml.stream.events.EndElement} {@code child}</li>
311
- * </ol>
312
- * </li>
313
- * </ol>
314
- */
315
- Flux <List <XMLEvent >> split (Flux <XMLEvent > xmlEventFlux , QName desiredName ) {
316
- return xmlEventFlux .handle (new SplitHandler (desiredName ));
317
- }
318
-
319
-
320
- private static class SplitHandler implements BiConsumer <XMLEvent , SynchronousSink <List <XMLEvent >>> {
321
-
322
- private final QName desiredName ;
323
-
324
- @ Nullable
325
- private List <XMLEvent > events ;
326
-
327
- private int elementDepth = 0 ;
328
-
329
- private int barrier = Integer .MAX_VALUE ;
330
-
331
- public SplitHandler (QName desiredName ) {
332
- this .desiredName = desiredName ;
333
- }
334
-
335
- @ Override
336
- public void accept (XMLEvent event , SynchronousSink <List <XMLEvent >> sink ) {
337
- if (event .isStartElement ()) {
338
- if (this .barrier == Integer .MAX_VALUE ) {
339
- QName startElementName = event .asStartElement ().getName ();
340
- if (this .desiredName .equals (startElementName )) {
341
- this .events = new ArrayList <>();
342
- this .barrier = this .elementDepth ;
343
- }
344
- }
345
- this .elementDepth ++;
346
- }
347
- if (this .elementDepth > this .barrier ) {
348
- Assert .state (this .events != null , "No XMLEvent List" );
349
- this .events .add (event );
350
- }
351
- if (event .isEndElement ()) {
352
- this .elementDepth --;
353
- if (this .elementDepth == this .barrier ) {
354
- this .barrier = Integer .MAX_VALUE ;
355
- Assert .state (this .events != null , "No XMLEvent List" );
356
- sink .next (this .events );
357
- }
358
- }
359
- }
360
- }
361
-
362
243
}
0 commit comments