当前位置: 首页 > 知识库问答 >
问题:

Jackson vs.Spring HATEOAS vs.多态性

郦磊
2023-03-14

当我想使用多态成员反序列化实体时,Jackson会抛出一个com.fasterxml.jackson.databind.JsonMappingException,抱怨缺少类型信息(...它实际上存在于JSON中-

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

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

我使用spring boot V 1.2.4。发布,这意味着jackson版本为2.4.6,Spring HATEOAS版本为0.16.0。释放。

例子:

我家里有一只宠物:

@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> {

}

为了确认所有这些东西都有效,我有一个测试...

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

    }

}

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

有什么建议吗?

共有1个答案

羊舌承天
2023-03-14

我要试着回答一半。

处理PUT(可能也是PATCH)时,spring-data-rest-webmvc将给定的JSON数据合并到现有实体中。在这样做的同时,它会从JSON树中剥离实体中不存在的所有属性,然后将其传递给JacksonObjectMapper。换句话说,当Jackson对您的对象进行反序列化时,您的@class属性已经消失。

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

由于类似的原因,使用JsonTypeInfo(use=JsonTypeInfo.Id.NAME,include=As.WRAPPER\u OBJECT)方法也不会起作用(除了这次删除了整个WRAPPER对象)。与原来的方法一样,GET和POST也可以正常工作。

整个事情看起来像一个错误或@JsonTypeInfospring-data-rest-webmvc情况下不支持。

也许其他人可以对此有更多的了解。

 类似资料:
  • 问题内容: 我想了解参数多态性(例如Java / Scala / C ++语言中的通用类/函数的多态性)与Haskell类型系统中的“即席”多态性之间的主要区别。我熟悉第一种语言,但是我从未与Haskell合作。 更确切地说: 例如Java中的类型推断算法与Haskell中的类型推断有何不同? 请给我举一个例子,这种情况可以用Java / Scala编写但不能用Haskell编写(根据这些平台的模

  • 多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。 对面向对象来说,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法。通过编译之后会变成两个不同的方法,在运行时谈不上多态。而运行时多态是动态的,它是通

  • 问题内容: 出于好奇,这是一个问题。 我知道,当我们通过其超类的引用调用子类对象的重写方法时,JVM会重视对象的类型而不是引用的类型。 这是我的简单代码: 如预期的那样,我得到了输出: 现在我的问题是,有什么方法可以使用引用h调用超类eat()方法,而不是子类?我知道这是一个与多态性定律背道而驰的问题,但是您永远不知道何时会需要这样做。 我试图将参考h转换为Animal,但没有运气。有任何想法吗?

  • 我有一个类动物和扩展动物的子类猫和狗。我有一个名为Zoo的类,其中有一个变量作为List; i、 e。 } zoo对象将包含可以是Animal对象或Animal子类的动物,下面是zoo类对象的JSON表示示例。 我必须将其转换为java对象,以便列表中的第一个动物被转换为类animal的对象,第二个被转换为类Cat的对象

  • 这是一个出于好奇的问题。 我知道,当我们通过引用子类对象的超类来调用子类对象的重写方法时,JVM重视对象的类型,而不是引用的类型。 这是我的简单代码: 正如预期的那样,我得到输出: 现在我的问题是,我们有没有办法使用引用h来调用超类ate()方法,而不是子类一?我知道这是一个有点违反多态定律的问题,但你永远不知道什么时候需要这样做。 我试着用打字机把参考h打到动物身上,但没有成功。有什么想法吗?

  • C++ 支持多态性。所谓多态性是指:通过继承相关的不同的类,他们的对象能够对同一个函数调用作出不同的响应。例如,如果类 Rectangle 是从类 Quadrilateral 派生出来的,那么类 Rectangle 的对象比类 Quadrilateral 的对象的更具体,对类 Quadfilateral 的对象的操作(如计算周长和面积)也能用在类 Rextangle 的对象上。 多态性是通过虚函数

  • 7.3.3 多态性 在 7.3.1 中定义的类 Student 和 Teacher 中,有一个同名的方法 getNum。虽然同名,但这 个方法在两个类中的行为是完全不同的:在 Student 中返回的是学号,而在 Teacher 中返回的 是学生人数。因此,当我们向一个对象 obj 发送 getNum 消息时,所得结果取决于 obj 的类型。 在 OOP 中,多个不同类的对象都支持相同的消息,但各

  • polymorphism这个词意味着有许多形式。 通常,当存在类的层次结构并且它们通过继承相关时,会发生多态性。 Objective-C多态意味着对成员函数的调用将导致执行不同的函数,具体取决于调用该函数的对象的类型。 考虑一下这个例子,我们有一个类Shape,它为所有形状提供基本接口。 Square和Rectangle派生自基类Shape。 我们有方法printArea来展示OOP特征polym