【问题标题】:How to leverage polymorphism in MOXy POJO binding with resource JSON consumption?如何利用 MOXy POJO 绑定中的多态性与资源 JSON 消耗?
【发布时间】:2015-08-26 16:14:36
【问题描述】:

使用Jersey 2.6MOXy 作为 JSON 提供者和 POJO 绑定。
尽管资源方法需要父类实例(方法参数),但如何发送一个会导致创建子类实例的 JSON 对象。

JSON 示例
下面的示例可以创建一个 Student 对象,并且可以将其传递给接受 Person 对象的资源。

{"name":"Jon Doe", "grade":"Excellent"}

资源 POST 方法

@POST
@Path("/test")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Person test(Person student) {
    System.out.println("Name: " + student.getName());
    System.out.println("Garde: " + ((Student)student).getGrade());
    return student;
}

具有以下 POJOs(省略了 getter 和 setter)。

    public class Person {
        String name;    
    } 

    public class Student extends Person {
        String grade;
    }

Web.XML

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>TestDrive</display-name>
    <servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <!-- 
            MOXy JSON provider is used with Jersey which is registered automatically when in classpath.
            Ref.: https://jersey.java.net/documentation/latest/media.html#json.moxy
        -->
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.web.main</param-value>
        </init-param>
        <init-param>
            <param-name>jersey.config.server.provider.scanning.recursive</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/rs/*</url-pattern>
    </servlet-mapping>
</web-app>

编辑

  1. 资源方法必须使用 Person 引用。
  2. Person 可以是抽象类,但不能是接口(它具有私有的非静态成员)。

【问题讨论】:

    标签: json rest jersey pojo moxy


    【解决方案1】:

    首先,我认为你需要问你这些问题:

    • 为什么要用Person作为参数,可以直接用Student吗?根据您的方法正在执行的操作,您需要知道您真正想要检索的是一个人,还是具体是一个学生。 test(Person person) VS test(Student student)

    • 您想将Person 用作类、抽象类还是接口?

    有多种解决方案。对于您将Person 保留为普通课程的情况,我有两种解决方案:

    1。 MessageBodyReader

    您可以使用自定义MessageBodyReader&lt;Person&gt; 来根据您的意愿反序列化您的 JSON。例如,您可以检查传递的 JSON 中是否存在 grade 字段。如果存在,您将返回Student,如果不存在,您将返回Person。这是使用 GSON 读取 JSON 的代码:

    import com.google.gson.stream.JsonReader;
    
    @Provider
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public class CustomObjectMapper implements MessageBodyReader<Person> {
    
        @Override
        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
            return Person.class.isAssignableFrom(type);
        }
    
        @Override
        public Person readFrom(Class<Person> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
            String personName = null;
            String studentGrade = null;
            JsonReader reader = new JsonReader(new InputStreamReader(entityStream, "UTF-8"));
            try {
                reader.beginObject();
                while (reader.hasNext()) {
                    String name = reader.nextName();
                    if (name.equals("name")) {
                        personName = reader.nextString();
                    } else if (name.equals("grade")) {
                        studentGrade = reader.nextString();
                    } else {
                        reader.skipValue();
                    }
                }
                reader.endObject();
            } finally {
                reader.close();
            }
            if (studentGrade != null) {
                return new Student(personName, studentGrade);
            }
            return new Person(personName);
        }
    }
    

    GSON maven 依赖

    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.3.1</version>
    </dependency>
    

    2。 POST 专用类

    在这个解决方案中,我们将创建一个名为PostPerson.java 的新类。该对象将成为您的方法test(PostPerson person) 的新参数。 此类与MessageBodyReader&lt;Person&gt; 具有相同的行为,但在我看来,它更具可读性和更易于使用。但是,这里我们将使用 Jackson 而不是 MOXy 以使用 Jackson 注释。

    PostPerson.java

    public class PostPerson {
    
        //your actual object deserialized
        private final Person person;
    
        //specify which constructor to use to create a PostPerson from JSON. (By default, it is using the default constructor)
        @JsonCreator
        public PostPerson(@JsonProperty("name") String name, @JsonProperty("grade") String grade) {
            if (grade == null) {
                person = new Person();
                person.setName(name);
            } else {
                person = new Student(name, grade);
            }
        }
    
        public Person getPerson() {
            return person;
        }
    }
    

    资源 POST 方法

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Person postname(PostPerson postPerson) {
        Person person = postPerson.getPerson();
        System.out.println(person.getName());
        System.out.println(((Student) person).getGrade());
        return person;
    }
    

    【讨论】:

    • 回答自己问的问题,第一个问题,是的,它必须是Person,第二个问题,不,你是对的,我正在将Person 改为抽象,但是在这个Jackson example我经过测试,父母是否抽象似乎并不重要,您是否知道MOXy中的类似行为,我正在考虑更改为Jackson,它会更容易找到支持。
    • 您的解决方案优雅且内容丰富,但在杰克逊,它是在没有构建服装 MessageBodyReader 或 POST 专用类的情况下处理的,这主要是我试图节省时间而不是构建的原因。
    • 然后尝试对@JsonDeserialize(as = xxx.class) 进行一些研究。或许能帮到你!
    猜你喜欢
    • 2018-12-23
    • 2015-10-30
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多