Convert a json to an array of objects in spring in

2019-07-21 06:48发布

We are returning an array of objects from a service (http outbound gateway) in json format and we need it serialized back into an array of objects/POJOS. First we tried simply serializing the POJO without any array lists by setting the XML configuration to

<int:json-to-object-transformer input-channel="testJsonToObjectChannel"
            output-channel="testChannel" type="com.that.service.service.test.ApplicationTestDomain" />

and had both the transformer and the http outbound gateway return the same object. However after converting it to an array of "ApplicationTestDomain" POJOs we are getting the error mentioned in the stack trace where it notably mentions

"No converter found capable of converting from type java.util.ArrayList<?> to type com.that.service.service.test.ApplicationTestDomain"

We also tried a simple array of strings and they were succesfully serialized and deserialzed as well, the issue only comes up when we try to serialize and deserialize an array of objects.

Any idea on what needs to be done so that can be resolved

The following is the service which returns the array list

@Service("applicationTestService")
public class ApplicationTestService {

    private static Logger logger = Logger.getLogger(ApplicationTestService.class);

    public ArrayList<ApplicationTestDomain> getTestThatData(Message<?> inMessage){
        ArrayList<ApplicationTestDomain> testData = new ArrayList<ApplicationTestDomain>();

        ApplicationTestDomain testDomain = new ApplicationTestDomain();
        testDomain.setId(1L);
        testDomain.setTotalPrice(100.00D);
        testDomain.setTotalTaxes(70.00D);
        testDomain.setTotalAll(70D);

        testData.add(testDomain);

        return testData;
    }

}

The following is the service which receives the array list

@MessageEndpoint("applicationDataTransformer")
public class ApplicationTransformer {

    public ApplicationResponse transformData(ArrayList<ApplicationTestDomain> response) {
        return new ApplicationResponse();
    }
}

The following is the xml configuration

<int-http:outbound-gateway request-channel="applicationConfigurationRequest" 
                           reply-channel="testJsonToObjectChannel"
                           url="http://localhost:8080/testapplication/services/application/testService"
                           http-method="GET"
                           expected-response-type="java.lang.String"/>


<int:json-to-object-transformer input-channel="testJsonToObjectChannel"
        output-channel="testChannel" type="java.util.ArrayList" />

<int:transformer input-channel="testChannel"
    ref="applicationDataTransformer"
    method="transformData"
    output-channel="applicationConfigurationResponse"/>         

The following is the exception stack trace

at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:52)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.messaging.MessageHandlingException: org.springframework.expression.AccessException: Problem invoking method: public com.that.domain.service.hotelavailability.HotelAvailabilityResponse com.that.transformer.service.ApplicationTransformer.transformData(java.util.ArrayList)
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:78)
    at org.springframework.integration.transformer.AbstractMessageProcessingTransformer.transform(AbstractMessageProcessingTransformer.java:64)
    at org.springframework.integration.transformer.MessageTransformingHandler.handleRequestMessage(MessageTransformingHandler.java:68)
    ... 83 more
Caused by: org.springframework.expression.AccessException: Problem invoking method: public com.that.domain.service.hotelavailability.HotelAvailabilityResponse com.that.transformer.service.ApplicationTransformer.transformData(java.util.ArrayList)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:67)
    at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:122)
    at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:44)
    at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:258)
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:84)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:111)
    at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:159)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:268)
    at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:142)
    at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:75)
    ... 85 more
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.util.ArrayList<?> to type java.util.ArrayList<com.that.service.service.test.ApplicationTestDomain> for value '[{id=1, totalPrice=100.0, totalTaxes=70.0, totalAll=70.0}]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.util.ArrayList<?> to type com.that.service.service.test.ApplicationTestDomain
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
    at org.springframework.integration.util.BeanFactoryTypeConverter.convertValue(BeanFactoryTypeConverter.java:123)
    at org.springframework.expression.spel.support.ReflectionHelper.convertArguments(ReflectionHelper.java:240)
    at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:57)
    ... 95 more
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.util.ArrayList<?> to type com.that.service.service.test.ApplicationTestDomain
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:291)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:177)
    at org.springframework.core.convert.support.CollectionToCollectionConverter.convert(CollectionToCollectionConverter.java:85)
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35)
    ... 99 more

Regards, MilindaD

1条回答
叼着烟拽天下
2楼-- · 2019-07-21 07:32

Your issues here that you just use raw java.util.ArrayList and you end up just with ArrayList<LinkedHashMap<String, Object>>, rather than ArrayList<LinkedHashMap<String, ApplicationTestDomain>>.

Try this trick:

type="com.that.service.service.test.ApplicationTestDomain[]"

The general stop here that we can't provide Class with generic specification.

But with that array trick there should automatic conversion from Array to Collection when Framework appies Message to arguments of your service method.

From other side <int-http:outbound-gateway> provides expected-response-type-expression, which can return ParameterizedTypeReference<?>.

Here is a trick how to let him know about your desired type:

<int:header-enricher>
        <int:header name="expectedResponseType">
            <int-groovy:script>
                <![CDATA[
                    new org.springframework.core.ParameterizedTypeReference<List<com.that.service.service.test.ApplicationTestDomain>>() {}
                ]]>
            </int-groovy:script>       
        </int:header>
</int:header-enricher>

<int-http:outbound-gateway request-channel="applicationConfigurationRequest" 
                       reply-channel="testJsonToObjectChannel"
                       url="http://localhost:8080/testapplication/services/application/testService"
                       http-method="GET"
                       expected-response-type-expression="headers.expectedResponseType"/>

Having this you allow to RestTemplate to convert response to the desired value using Jackson, of course. And from here there is no more reason to use <int:json-to-object-transformer>, because you already have your List of POJOs.

查看更多
登录 后发表回答