如何将Jackson TypeReference用于泛型类

时间:2016-11-20 21:11:29

标签: java angularjs generics jackson

我是新来的,知道下面的问题很多时候已经讨论过了,但我找不到明确解决问题的方法......

所以,我在一个由两部分组成的应用程序上工作: - AngularJS的前端 - Spring MVC的后端 两部分都以JSON格式与杰克逊交换。 将JSON与View Object(VO)关联起来

所以我在FrontEnd上面有以下Javascript函数:

 		 /**
 		 * Reference save.
 		 * @param reference The reference to save
 		 * @param type Type of reference
 		 */
 		 save: function(reference,type) {
 		 	return $http.post('myApplication/reference/save/'+ type, reference);
 		 }

将数据发送到下面的Java函数:

         **
     * Reference Controller.
     * @param <VO> Reference
     * @param <E> Entity
     */
    @RequestMapping("/reference/")
    @Controller
    public class ReferenceController<VO extends AbstractReferenceEntityVO, E extends AbstractReferenceEntity> {

    /** Reference management service. */
    @Autowired
    private ReferenceService<VO, E> referenceService;

    /**
     * Reference save method.
     * @param referenceVO View Object of the reference to save
     * @param type Reference type
     * @return VO
     */
    @ResponseBody
    @RequestMapping(value = "/save/{type}", method = RequestMethod.POST,
            headers = "Accept=application/json")
    public VO save(@Validated @RequestBody final VO referenceVO,
            @PathVariable final String type)
    {
        return this.referenceService.save(type, referenceVO);

    }
}

这个JAVA类是通用的,因为这个控制器可以接收/发送2种ReferenceVO(VO1和VO2),它们都是从Abstract类(AbstractReferenceEntityVO)继承。

今天我在尝试调用此保存功能时遇到了以下问题:

    *Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of myApplication.AbstractReferenceEntityVO, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
     at [Source: java.io.PushbackInputStream@1a956b3d; line: 1, column: 1]
        at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
        at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:889)
        at 
com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:139)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3702)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2798)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:205)
    ... 88 more*

经过研究,我发现我必须使用jackson“TypeReference”来管理Generic类。所以我想我必须指定杰克逊继承了必须用于映射的VO所以我需要首先对我的引用的“类型”做一个条件,以便与正确的VO联系...

这是我的ObjectMapper&amp; XML配置:

	<mvc:annotation-driven conversion-service="applicationConversionService">
	  	<mvc:message-converters>
			<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
 				<property name="objectMapper">
					<bean class="myApplication.CustomObjectMapper" />
				</property>
			</bean>
		</mvc:message-converters>
	</mvc:annotation-driven>

/**
 * Mapper JSON objets.
 */
public class CustomObjectMapper
    extends ObjectMapper
{
    /** Id. */
    private static final long serialVersionUID = 6488036405547458525L;

    /** Constructor. */
    public PlageObjectMapper()
    {
        super();
        this.registerModule(new JodaModule());
        this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        this.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        TypeReference ref = new TypeReference<AbstractReferenceEntityVO>() { 
            // if(type== "TYPE_1") {
            //    TODO
            // } else {
            //    TODO
            // }
        };
    }
}

是否有人必须做类似的事情?或者如何在我的情况下使用TypeReference?说实话,我有点迷失了怎么做!

非常感谢你的帮助!

1 个答案:

答案 0 :(得分:0)

您需要将类型标识符作为json中的字段传递。使用@JsonTypeInfo注释基类以指定字段的名称和要使用的标识符。默认情况下,如果您使用类名作为标识符,则字段名称为@class

有关详细信息,请参阅polymorphic deserialisation上的Wiki页面。

示例:

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.xml.sax.SAXException;

import javax.xml.transform.TransformerException;
import java.io.IOException;

public class Scratch {

    private static ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) throws IOException, TransformerException, SAXException {
        System.out.println(mapper.readValue("{\"@class\": \"Scratch$B\", \"payload\": \"hello, world.\"}", A.class));
        System.out.println(mapper.readValue("{\"@class\": \"Scratch$C\", \"payload\": \"hello again.\", \"extra\": 1}", A.class));
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    abstract public static class A {
        abstract public String getPayload();

        @Override
        public String toString() {
            try {
                return "I'm a " + getClass().getSimpleName() + ": " + mapper.writeValueAsString(this);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static class B extends A {
        private String payload;

        @Override
        public String getPayload() {
            return payload;
        }

        public void setPayload(String payload) {
            this.payload = payload;
        }
    }

    public static class C extends B {
        private int extra;

        public int getExtra() {
            return extra;
        }

        public void setExtra(int extra) {
            this.extra = extra;
        }
    }
}