2015-06-15 10 views
5

Когда я хочу десериализовать Entity с членом полиморфа, Джексон бросает com.fasterxml.jackson.databind.JsonMappingException, жалуясь на отсутствующую информацию о типе (... которая фактически присутствует в примере JSON -> см. Пример) ,Jackson vs. Spring HATEOAS против полиморфизма

Unexpected token (END_OBJECT), expected FIELD_NAME: missing property '@class' that is to contain type id (for class demo.animal.Animal)\n at [Source: N/A; line: -1, column: -1] (through reference chain: demo.home.Home[\"pet\"])" 

Все фактическая работа осуществляется с помощью PagingAndSortingRepository от Spring HATEOAS.

Я использую весеннюю загрузку V 1.2.4.RELEASE, что означает, что jackson - это V 2.4.6, а Spring HATEOAS - V 0.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; 
    } 

} 

Это 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 { 

} 

Тогда это удобно PagingAndSortingRepository, что позволяет мне получить доступ в мой дом через REST/HATEOAS ...

@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"))); 

    } 

} 

Интересно, я могу создать свой дом с кошкой, как домашнее животное, но когда я хочу, чтобы обновить имя кошки не может десериализации J SON больше ...

Любые предложения?

+0

Почему вы обновляете 'home', когда хотите изменить имя животного? Ваш 'put' создаст новое животное. – zeroflagL

ответ

4

Я собираюсь попытаться получить половину ответа.

При обработке PUT (возможно, PATCH) spring-data-rest-webmvc объединяет данные JSON в существующую сущность. При этом он удаляет все свойства, которые не существуют в объекте, из дерева JSON до, передавая его в Jackson ObjectMapper. Другими словами, ваше имущество @class исчезло к тому времени, когда Джексон получит десериализацию вашего объекта.

Вы можете обойти это (для целей тестирования/демонстрации), добавив свой объект @class в качестве фактического имущества для вашей организации (вы должны переименовать его, скажем, classname). Теперь все будет работать нормально, однако у вашей организации теперь есть бесполезное свойство classname, которое, вероятно, не то, что вы хотите.

Использование метода @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.WRAPPER_OBJECT) также не будет работать по той же причине (за исключением того, что весь объект-оболочка будет удален). Также, как и в случае с оригинальным подходом, GET и POST будут работать нормально.

Все это выглядит как ошибка или @JsonTypeInfo не поддерживается в spring-data-rest-webmvc ситуации.

Возможно, кто-то еще может пролить свет на это.

Смежные вопросы