All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http.converter.xml;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.xml.bind.JAXBException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import javax.xml.transform.Source;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.xml.StaxUtils;

/**
 * An {@code HttpMessageConverter} that can read XML collections using JAXB2.
 *
 * 

This converter can read {@linkplain Collection collections} that contain classes * annotated with {@link XmlRootElement} and {@link XmlType}. Note that this converter * does not support writing. * * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.2 * @param the converted object type */ @SuppressWarnings("rawtypes") public class Jaxb2CollectionHttpMessageConverter extends AbstractJaxb2HttpMessageConverter implements GenericHttpMessageConverter { private final XMLInputFactory inputFactory = createXmlInputFactory(); /** * Always returns {@code false} since Jaxb2CollectionHttpMessageConverter * required generic type information in order to read a Collection. */ @Override public boolean canRead(Class clazz, @Nullable MediaType mediaType) { return false; } /** * {@inheritDoc} *

Jaxb2CollectionHttpMessageConverter can read a generic * {@link Collection} where the generic type is a JAXB type annotated with * {@link XmlRootElement} or {@link XmlType}. */ @Override public boolean canRead(Type type, @Nullable Class contextClass, @Nullable MediaType mediaType) { if (!(type instanceof ParameterizedType)) { return false; } ParameterizedType parameterizedType = (ParameterizedType) type; if (!(parameterizedType.getRawType() instanceof Class)) { return false; } Class rawType = (Class) parameterizedType.getRawType(); if (!(Collection.class.isAssignableFrom(rawType))) { return false; } if (parameterizedType.getActualTypeArguments().length != 1) { return false; } Type typeArgument = parameterizedType.getActualTypeArguments()[0]; if (!(typeArgument instanceof Class)) { return false; } Class typeArgumentClass = (Class) typeArgument; return (typeArgumentClass.isAnnotationPresent(XmlRootElement.class) || typeArgumentClass.isAnnotationPresent(XmlType.class)) && canRead(mediaType); } /** * Always returns {@code false} since Jaxb2CollectionHttpMessageConverter * does not convert collections to XML. */ @Override public boolean canWrite(Class clazz, @Nullable MediaType mediaType) { return false; } /** * Always returns {@code false} since Jaxb2CollectionHttpMessageConverter * does not convert collections to XML. */ @Override public boolean canWrite(@Nullable Type type, @Nullable Class clazz, @Nullable MediaType mediaType) { return false; } @Override protected boolean supports(Class clazz) { // should not be called, since we override canRead/Write throw new UnsupportedOperationException(); } @Override protected T readFromSource(Class clazz, HttpHeaders headers, Source source) throws Exception { // should not be called, since we return false for canRead(Class) throw new UnsupportedOperationException(); } @Override @SuppressWarnings("unchecked") public T read(Type type, @Nullable Class contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { ParameterizedType parameterizedType = (ParameterizedType) type; T result = createCollection((Class) parameterizedType.getRawType()); Class elementClass = (Class) parameterizedType.getActualTypeArguments()[0]; try { Unmarshaller unmarshaller = createUnmarshaller(elementClass); XMLStreamReader streamReader = this.inputFactory.createXMLStreamReader(inputMessage.getBody()); int event = moveToFirstChildOfRootElement(streamReader); while (event != XMLStreamReader.END_DOCUMENT) { if (elementClass.isAnnotationPresent(XmlRootElement.class)) { result.add(unmarshaller.unmarshal(streamReader)); } else if (elementClass.isAnnotationPresent(XmlType.class)) { result.add(unmarshaller.unmarshal(streamReader, elementClass).getValue()); } else { // should not happen, since we check in canRead(Type) throw new HttpMessageNotReadableException( "Cannot unmarshal to [" + elementClass + "]", inputMessage); } event = moveToNextElement(streamReader); } return result; } catch (XMLStreamException ex) { throw new HttpMessageNotReadableException( "Failed to read XML stream: " + ex.getMessage(), ex, inputMessage); } catch (UnmarshalException ex) { throw new HttpMessageNotReadableException( "Could not unmarshal to [" + elementClass + "]: " + ex.getMessage(), ex, inputMessage); } catch (JAXBException ex) { throw new HttpMessageConversionException("Invalid JAXB setup: " + ex.getMessage(), ex); } } /** * Create a Collection of the given type, with the given initial capacity * (if supported by the Collection type). * @param collectionClass the type of Collection to instantiate * @return the created Collection instance */ @SuppressWarnings("unchecked") protected T createCollection(Class collectionClass) { if (!collectionClass.isInterface()) { try { return (T) ReflectionUtils.accessibleConstructor(collectionClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException( "Could not instantiate collection class: " + collectionClass.getName(), ex); } } else if (List.class == collectionClass) { return (T) new ArrayList(); } else if (SortedSet.class == collectionClass) { return (T) new TreeSet(); } else { return (T) new LinkedHashSet(); } } private int moveToFirstChildOfRootElement(XMLStreamReader streamReader) throws XMLStreamException { // root int event = streamReader.next(); while (event != XMLStreamReader.START_ELEMENT) { event = streamReader.next(); } // first child event = streamReader.next(); while ((event != XMLStreamReader.START_ELEMENT) && (event != XMLStreamReader.END_DOCUMENT)) { event = streamReader.next(); } return event; } private int moveToNextElement(XMLStreamReader streamReader) throws XMLStreamException { int event = streamReader.getEventType(); while (event != XMLStreamReader.START_ELEMENT && event != XMLStreamReader.END_DOCUMENT) { event = streamReader.next(); } return event; } @Override public void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { throw new UnsupportedOperationException(); } @Override protected void writeToResult(T t, HttpHeaders headers, Result result) throws Exception { throw new UnsupportedOperationException(); } /** * Create an {@code XMLInputFactory} that this converter will use to create * {@link javax.xml.stream.XMLStreamReader} and {@link javax.xml.stream.XMLEventReader} * objects. *

Can be overridden in subclasses, adding further initialization of the factory. * The resulting factory is cached, so this method will only be called once. * @see StaxUtils#createDefensiveInputFactory() */ protected XMLInputFactory createXmlInputFactory() { return StaxUtils.createDefensiveInputFactory(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy