Java Jackson vs.Spring HATEOAS vs.多态性

Java Jackson vs.Spring HATEOAS vs.多态性,java,polymorphism,jackson,spring-boot,spring-hateoas,Java,Polymorphism,Jackson,Spring Boot,Spring Hateoas,当我想用一个polymorph成员反序列化一个实体时,Jackson抛出一个com.fasterxml.Jackson.databind.JsonMappingException,抱怨缺少类型信息(…它实际上存在于JSON->参见示例) 所有实际工作都由Spring HATEOAS的分页和排序存储库完成 我使用SpringBootV1.2.4.RELEASE,这意味着jackson是V2.4.6,SpringHateoas是V0.16.0.RELEASE 例如: 我家里有一只宠物: @Entit

当我想用一个polymorph成员反序列化一个实体时,Jackson抛出一个
com.fasterxml.Jackson.databind.JsonMappingException
,抱怨缺少类型信息(…它实际上存在于JSON->参见示例)

所有实际工作都由Spring HATEOAS的分页和排序存储库完成

我使用SpringBootV1.2.4.RELEASE,这意味着jackson是V2.4.6,SpringHateoas是V0.16.0.RELEASE

例如:

我家里有一只宠物:

@Entity
public class Home {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @OneToOne(cascade = {CascadeType.ALL})
    private Animal pet;

    public Animal getPet() {
        return pet;
    }

    public void setPet(Animal pet) {
        this.pet = pet;
    }

}
那只宠物是某种动物——在这种情况下是猫还是狗。它的类型由@class属性标识

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

@Entity
public class Cat extends Animal {

}

@Entity
public class Dog extends Animal {

}
还有这个方便的分页和排序存储库,它允许我通过REST/HATEOAS访问我的家

@RepositoryRestResource(collectionResourceRel = "home", path = "home")
public interface HomeRepository extends PagingAndSortingRepository<Home, Integer> {

}
有趣的是,我可以用猫作为宠物来创建我的家,但是当我想更新猫的名字时,它就不能再反序列化JSON了


有什么建议吗?

我试着回答一半

在处理PUT(可能还有补丁)时,
spring数据rest webmvc
将给定的JSON数据合并到现有实体中。执行此操作时,它将从JSON树中剥离实体中不存在的所有属性,然后将其传递给Jackson
ObjectMapper
。换句话说,当Jackson开始对对象进行反序列化时,您的
@class
属性就消失了

您可以通过将
@class
属性作为实际属性添加到实体(当然,您必须重命名它,比如
classname
)来解决此问题(出于测试/演示目的)。现在一切正常,但是您的实体现在有一个无用的
classname
属性,这可能不是您想要的

使用
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=As.WRAPPER\u OBJECT)
方法也不起作用,原因类似(除了这次删除了整个包装器对象)。与最初的方法一样,GET和POST也可以很好地工作

整个问题看起来像一个bug或
@JsonTypeInfo
spring-data-rest-webmvc
情况下不受支持


也许其他人可以对此提供更多信息。

当你想更改动物的名字时,为什么要更新
主页?你的
put
将创建一个新的动物。
@RepositoryRestResource(collectionResourceRel = "home", path = "home")
public interface HomeRepository extends PagingAndSortingRepository<Home, Integer> {

}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = DemoApplication.class)
@WebAppConfiguration
public class HomeIntegrationTest {

    @Autowired
    private WebApplicationContext ctx;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = webAppContextSetup(ctx).build();
    }

    @Test
    public void testRename() throws Exception {

        // I create my home with some cat...
        // http://de.wikipedia.org/wiki/Schweizerdeutsch#Wortschatz -> Büsi
        MockHttpServletRequestBuilder post = post("/home/")
                .content("{\"pet\": {\"@class\": \"demo.animal.Cat\", \"name\": \"Büsi\"}}");
        mockMvc.perform(post).andDo(print()).andExpect(status().isCreated());

        // Confirm that the POST request works nicely, so the JSON thingy is correct...
        MockHttpServletRequestBuilder get1 = get("/home/").accept(MediaType.APPLICATION_JSON);
        mockMvc.perform(get1).andDo(print()).andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$._embedded.home", hasSize(1)))
                .andExpect(jsonPath("$._embedded.home[0].pet.name", is("Büsi")));

        // Now the interesting part: let's give that poor kitty a proper name...
        MockHttpServletRequestBuilder put = put("/home/1")
                .content("{\"pet\": {\"@class\": \"demo.animal.Cat\", \"name\": \"Beauford\"}}");
        mockMvc.perform(put).andDo(print()).andExpect(status().isNoContent());
        // PUT will thow JsonMappingException exception about an missing "@class"...

        MockHttpServletRequestBuilder get2 = get("/home/").accept(MediaType.APPLICATION_JSON);
        mockMvc.perform(get2).andDo(print()).andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$._embedded.home", hasSize(1)))
                .andExpect(jsonPath("$._embedded.home[0].pet.name", is("Beaufort")));

    }

}