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

org.springframework.integration.json.JsonToObjectTransformer Maven / Gradle / Ivy

There is a newer version: 6.2.4
Show newest version
/*
 * Copyright 2002-2020 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.integration.json;

import java.io.IOException;
import java.io.UncheckedIOException;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.ResolvableType;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.mapping.support.JsonHeaders;
import org.springframework.integration.support.json.JsonObjectMapper;
import org.springframework.integration.support.json.JsonObjectMapperProvider;
import org.springframework.integration.transformer.AbstractTransformer;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;

/**
 * Transformer implementation that converts a JSON string payload into an instance of the
 * provided target Class. By default this transformer uses
 * {@linkplain org.springframework.integration.support.json.JsonObjectMapperProvider}
 * factory to get an instance of Jackson JSON-processor
 * if jackson-databind lib is present on the classpath. Any other {@linkplain JsonObjectMapper}
 * implementation can be provided.
 * 

Since version 3.0, you can omit the target class and the target type can be * determined by the {@link JsonHeaders} type entries - including the contents of a * one-level container or map type. *

The type headers can be classes or fully-qualified class names. *

Since version 5.2.6, a SpEL expression option is provided to let to build a target *{@link ResolvableType} somehow externally. * * @author Mark Fisher * @author Artem Bilan * * @since 2.0 * * @see JsonObjectMapper * @see org.springframework.integration.support.json.JsonObjectMapperProvider * @see ResolvableType */ public class JsonToObjectTransformer extends AbstractTransformer implements BeanClassLoaderAware { private final ResolvableType targetType; private final JsonObjectMapper jsonObjectMapper; private ClassLoader classLoader; private Expression valueTypeExpression = new FunctionExpression>((message) -> obtainResolvableTypeFromHeadersIfAny(message.getHeaders(), this.classLoader)); private EvaluationContext evaluationContext; public JsonToObjectTransformer() { this((Class) null); } public JsonToObjectTransformer(@Nullable Class targetClass) { this(ResolvableType.forClass(targetClass)); } /** * Construct an instance based on the provided {@link ResolvableType}. * @param targetType the {@link ResolvableType} to use. * @since 5.2 */ public JsonToObjectTransformer(ResolvableType targetType) { this(targetType, null); } public JsonToObjectTransformer(@Nullable JsonObjectMapper jsonObjectMapper) { this((Class) null, jsonObjectMapper); } public JsonToObjectTransformer(@Nullable Class targetClass, @Nullable JsonObjectMapper jsonObjectMapper) { this(ResolvableType.forClass(targetClass), jsonObjectMapper); } /** * Construct an instance based on the provided {@link ResolvableType} and {@link JsonObjectMapper}. * @param targetType the {@link ResolvableType} to use. * @param jsonObjectMapper the {@link JsonObjectMapper} to use. * @since 5.2 */ public JsonToObjectTransformer(ResolvableType targetType, @Nullable JsonObjectMapper jsonObjectMapper) { Assert.notNull(targetType, "'targetType' must not be null"); this.targetType = targetType; this.jsonObjectMapper = (jsonObjectMapper != null) ? jsonObjectMapper : JsonObjectMapperProvider.newInstance(); } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; if (this.jsonObjectMapper instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) this.jsonObjectMapper).setBeanClassLoader(classLoader); } } /** * Configure a SpEL expression to evaluate a {@link ResolvableType} * to instantiate the payload from the incoming JSON. * By default this transformer consults {@link JsonHeaders} in the request message. * If this expression returns {@code null} or {@link ResolvableType} building throws a * {@link ClassNotFoundException}, this transformer falls back to the provided {@link #targetType}. * This logic is present as an expression because {@link JsonHeaders} may not have real class values, * but rather some type ids which have to be mapped to target classes according some external registry. * @param valueTypeExpressionString the SpEL expression to use. * @since 5.2.6 */ public void setValueTypeExpressionString(String valueTypeExpressionString) { setValueTypeExpression(EXPRESSION_PARSER.parseExpression(valueTypeExpressionString)); } /** * Configure a SpEL {@link Expression} to evaluate a {@link ResolvableType} * to instantiate the payload from the incoming JSON. * By default this transformer consults {@link JsonHeaders} in the request message. * If this expression returns {@code null} or {@link ResolvableType} building throws a * {@link ClassNotFoundException}, this transformer falls back to the provided {@link #targetType}. * This logic is present as an expression because {@link JsonHeaders} may not have real class values, * but rather some type ids which have to be mapped to target classes according some external registry. * @param valueTypeExpression the SpEL {@link Expression} to use. * @since 5.2.6 */ public void setValueTypeExpression(Expression valueTypeExpression) { this.valueTypeExpression = valueTypeExpression; } @Override public String getComponentType() { return "json-to-object-transformer"; } @Override protected void onInit() { super.onInit(); this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory()); } @Override protected Object doTransform(Message message) { ResolvableType valueType = obtainResolvableType(message); boolean removeHeaders = false; if (valueType != null) { removeHeaders = true; } else { valueType = this.targetType; } Object result; try { result = this.jsonObjectMapper.fromJson(message.getPayload(), valueType); } catch (IOException e) { throw new UncheckedIOException(e); } if (removeHeaders) { return getMessageBuilderFactory() .withPayload(result) .copyHeaders(message.getHeaders()) .removeHeaders(JsonHeaders.HEADERS.toArray(new String[0])) .build(); } else { return result; } } @Nullable private ResolvableType obtainResolvableType(Message message) { try { return this.valueTypeExpression.getValue(this.evaluationContext, message, ResolvableType.class); } catch (Exception ex) { if (ex.getCause() instanceof ClassNotFoundException) { logger.debug("Cannot build a ResolvableType from the request message '" + message + "' evaluating expression '" + this.valueTypeExpression.getExpressionString() + "'", ex); return null; } else { throw ex; } } } @Nullable private static ResolvableType obtainResolvableTypeFromHeadersIfAny(MessageHeaders headers, ClassLoader classLoader) { Object valueType = headers.get(JsonHeaders.RESOLVABLE_TYPE); Object typeIdHeader = headers.get(JsonHeaders.TYPE_ID); if (!(valueType instanceof ResolvableType) && typeIdHeader != null) { valueType = JsonHeaders.buildResolvableType(classLoader, typeIdHeader, headers.get(JsonHeaders.CONTENT_TYPE_ID), headers.get(JsonHeaders.KEY_TYPE_ID)); } return valueType instanceof ResolvableType ? (ResolvableType) valueType : null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy