2015-06-15 34 views
5

Kiedy chcę deserializować Entity z członkiem polimorfu, Jackson rzuca com.fasterxml.jackson.databind.JsonMappingException, narzekając na brakujące informacje o typie (... które faktycznie jest obecne w JSON -> zobacz przykład) .Jackson vs Spring HATEOAS vs. Polimorfizm

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\"])" 

Cała rzeczywista praca jest wykonywana przez PagingAndSortingRepository z Spring HATEOAS.

Używam sprężynowo-rozruchowego V 1.2.4.RELEASE, co oznacza, że ​​jackson to V 2.4.6, a Spring HATEOAS to V 0.16.0.RELEASE.

Przykład:

mam zwierzaka w domu:

@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; 
    } 

} 

że PET jest jakieś zwierzę - w tym przypadku albo kot lub pies. To typ jest identyfikowana przez właściwość @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 { 

} 

Wtedy nie jest to przydatny PagingAndSortingRepository, który umożliwia mi dostęp do mojego domu poprzez REST/hateoas ...

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

} 

Aby potwierdzić wszystko że rzeczy działa, mam test w miejscu ...

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

    } 

} 

ciekawe mogę utworzyć mój dom z kotem jako zwierzę domowe, ale gdy chcę zaktualizować nazwę kot nie może deserializowania J SYN już ...

Jakieś sugestie?

+0

Dlaczego aktualizujesz 'home', kiedy chcesz zmienić nazwę zwierzęcia? Twoje 'put' stworzy nowe zwierzę. – zeroflagL

Odpowiedz

4

Mam zamiar podjąć połowiczną odpowiedź.

Podczas przetwarzania PUT (prawdopodobnie również PATCH), spring-data-rest-webmvc łączy dane JSON w istniejącą całość. Czyniąc to, usuwa wszystkie właściwości, które nie istnieją w obiekcie z drzewa JSON przed przekazaniem go do Jackson przekazując je Jacksonowi . Innymi słowy, twoja właściwość @class zniknęła, gdy Jackson uzyska deserializację obiektu.

Możesz obejść to (dla celów testowych/demonstracyjnych), dodając własność @class jako rzeczywistą właściwość do twojego podmiotu (musisz oczywiście zmienić jej nazwę, na przykład classname). Teraz wszystko będzie działało dobrze, ale twoja istota ma teraz inną, nieprzydatną właściwość, która prawdopodobnie nie jest tym, czego potrzebujesz.

Korzystanie z podejścia również nie zadziała, z podobnego powodu (z wyjątkiem tego czasu usunięty został cały obiekt opakowania). Podobnie jak w przypadku oryginalnego podejścia, GET i POST będą działać poprawnie.

Cała sprawa wygląda jak błąd lub @JsonTypeInfo nie jest obsługiwana w sytuacji spring-data-rest-webmvc.

Może ktoś inny może rzucić więcej światła na ten temat.