【问题标题】:Jersey: read uploaded file as JSON泽西岛:将上传的文件读取为 JSON
【发布时间】:2016-05-16 12:58:25
【问题描述】:

我可以通过以下方式上传球衣中的文件:

@POST
    @Consumes( MediaType.MULTIPART_FORM_DATA)
    public void indexFile(
            @FormDataParam("file") InputStream uploadedFile,
            @FormDataParam("file") FormDataContentDisposition fileInfo){
        System.out.println(fileInfo.getFileName());
        DAO.indexFileToSolr(uploadedFile);
    }

这将文件作为输入流提供给我。 此文件包含以下格式的 json 对象数组:

[
    {
        id: 1,
        title: "someTitle"
    }
]

我还有一个 POJO/Java 类代表这个对象,如下所示:

@XmlRootElement
public class MyObj{
    private int id;
    private String title;

   //getters and setters
}

我正在使用jersey-media-moxy 在传入请求或传出响应时在 POJO 和 JSON/XML 之间自动转换。 (我不需要为此写任何代码,我只需要指定请求是JSON格式,或者响应应该是JSON,序列化/反序列化由jersey-media-moxy处理。)

问题是,我无法在文件上传方法中指定此行为,我看不出有办法告诉 jersey 或 jersey-media-moxy 将文件序列化为 java 对象。

我在jersey-media-moxy 中找不到任何方法来实际读取 InputStream 并将其序列化为 POJO。

注意:我可能可以使用jackson 来读取 json 文件并创建 Java 对象列表,但我不太愿意这样做,首先因为这需要另一个库(如果我能做到那就太好了这与 jersey-media-mosy)其次,我不确定是否有一种简单的方法可以告诉 jersey 这个文件包含 Json 对象,所以,反序列化它。

【问题讨论】:

    标签: java json jersey-2.0


    【解决方案1】:

    如果您可以让客户端为 JSON 的单个部分设置 Content-Type 标头,那么处理它是微不足道的。使用 multipart,每个部分都可以有自己的Content-Type。例如,原始多部分请求的一部分可能看起来像

    --Boundary_1_1938025186_1463410894758
    Content-Type: application/json
    Content-Disposition: form-data; name="beans"
    
    [ {"name": "peeskillet"} ]
    --Boundary_1_1938025186_1463410894758--
    

    您可以看到,对于这个特定部分,Content-Type 设置为 application/json。如果你可以从客户端得到这个,那么你需要做的就是像普通的 JSON 请求一样拥有一个 POJO 参数

    @POST
    @Path("with-content-type")
    public List<Bean> post1(@FormDataParam("beans") List<Bean> beans) {
        return beans;
    }
    

    如果您尝试使用上述方法,并且客户端没有设置Content-Type,那么它将默认为text/plain,结果很奇怪。他们并不像预期的那样。根据我的测试,似乎 bean 属性设置了整个原始 JSON。

    我们可以做的是获得一个预反序列化的正文部分,明确设置Content-Type,然后反序列化它。

    @POST
    @Path("no-content-type")
    public Bean[] post2(@FormDataParam("beans") FormDataBodyPart bodyPart) throws IOException {
        bodyPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
        Bean[] beans = bodyPart.getEntityAs(Bean[].class);
        return beans;
    }
    

    注意,我使用Bean[],而不是List&lt;Bean&gt;。试图获得List&lt;Bean&gt; 的问题是我们不能做bodyPart.getEntityAs(List.class),因为MOXy 需要知道泛型类型。所以我们只是把它作为一个数组。如果客户端要设置Content-TypeBean[] 也可以用作方法参数,而不是像第一个示例中的 List&lt;Bean&gt;

    下面是一个完整的测试用例,使用Jersey Test Framework

    import java.io.IOException;
    import java.util.List;
    import java.util.logging.Logger;
    
    import javax.ws.rs.Consumes;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.client.Entity;
    import javax.ws.rs.core.GenericType;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.xml.bind.annotation.XmlRootElement;
    
    import org.glassfish.jersey.client.ClientConfig;
    import org.glassfish.jersey.filter.LoggingFilter;
    import org.glassfish.jersey.media.multipart.FormDataBodyPart;
    import org.glassfish.jersey.media.multipart.FormDataMultiPart;
    import org.glassfish.jersey.media.multipart.FormDataParam;
    import org.glassfish.jersey.media.multipart.MultiPart;
    import org.glassfish.jersey.media.multipart.MultiPartFeature;
    import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
    import org.glassfish.jersey.server.ResourceConfig;
    import org.glassfish.jersey.test.JerseyTest;
    import org.junit.Test;
    
    import static org.hamcrest.CoreMatchers.is;
    import static org.hamcrest.core.IsNull.notNullValue;
    import static org.junit.Assert.assertThat;
    
    /**
     * Run this like any other JUnit test. Only two required dependencies.
     * 
     *  <dependency>
     *      <groupId>org.glassfish.jersey.test-framework.providers</groupId>
     *      <artifactId>jersey-test-framework-provider-inmemory</artifactId>
     *      <version>2.22.1</version>
     *      <scope>test</scope>
     *  </dependency>
     *      <dependency>
     *      <groupId>org.glassfish.jersey.media</groupId>
     *      <artifactId>jersey-media-moxy</artifactId>
     *      <version>2.22.1</version>
     *      <scope>test</scope>
     *  </dependency>
     *
     * @author Paul Samsotha
     */
    public class MoxyMultipartTest extends JerseyTest {
    
        @XmlRootElement
        public static class Bean {
    
            private String name;
    
            public Bean() {
            }
    
            public Bean(String name) {
                this.name = name;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            @Override
            public String toString() {
                return this.name;
            }
        }
    
        @Path("test")
        @Consumes(MediaType.MULTIPART_FORM_DATA)
        @Produces(MediaType.APPLICATION_JSON)
        public static class TestResource {
    
            @POST
            @Path("with-content-type")
            public Bean[] post1(@FormDataParam("beans") Bean[] beans) {
                return beans;
            }
    
            @POST
            @Path("no-content-type")
            public Bean[] post2(@FormDataParam("beans") FormDataBodyPart bodyPart) throws IOException {
                bodyPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
                Bean[] beans = bodyPart.getEntityAs(Bean[].class);
                return beans;
            }
        }
    
        @Override
        public void configureClient(ClientConfig config) {
            config.register(MultiPartFeature.class);
            config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
        }
    
        @Override
        public ResourceConfig configure() {
            return new ResourceConfig(TestResource.class)
                    .register(MultiPartFeature.class)
                    .register(MoxyJsonFeature.class);
        }
    
        final String json = "[ {\"name\": \"peeskillet\"} ]";
    
        @Test
        public void testSettingContentType() {
            final MultiPart multiPart = new FormDataMultiPart()
                    .field("beans", json, MediaType.APPLICATION_JSON_TYPE);
            final Response response = target("test/with-content-type")
                    .request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA));
    
            assertResponseHasBeans(response);
        }
    
        @Test
        public void testWithoutSettingContentType() {
            final MultiPart multiPart = new FormDataMultiPart()
                    .field("beans", json); // No Content-Type
            final Response response = target("test/no-content-type")
                    .request().post(Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA));
    
            assertResponseHasBeans(response);
        }
    
        private void assertResponseHasBeans(Response response) {
            final List<Bean> beans = response.readEntity(new GenericType<List<Bean>>() {
            });
            assertThat(beans, is(notNullValue()));
            assertThat(beans.isEmpty(), is(false));
    
            assertThat(beans.get(0).getName(), is("peeskillet"));
        }
    }
    

    【讨论】:

    • 我没有任何其他部分,我只有一个文件。该文件包含 json 对象,但我不知道如何从该文件中读取 json 并创建 Java 对象列表。
    • 您无需阅读任何内容。泽西岛将为您阅读。请花时间阅读答案,测试一下。如果测试后仍有问题,请告诉我
    • 我阅读了答案,您似乎建议在从客户端发送到服务器的多部分表单数据上设置内容类型。但这不是(完全是)我的用例,我正在尝试一个简单的文件上传,并且我能够接收整个文件,内容类型是 application/octet-stream,现在这个文件包含 json 对象,我需要在数组或列表中读取它。
    • 使用 multpart 时,无论您是只发送一个部分(一个文件)还是一堆部分(多个文件),总有“部分”。无论哪种方式,每个部分都可以设置一个内容类型,但主要请求仍然是 multpart/form-data,各个部分都有自己的类型。我上面的示例请求只是多部分请求中的一个部分,它不是完整的请求。
    • 当 jersey 反序列化多部分时,每个部分都以与常规请求相同的方式反序列化。对于常规请求,jersey 知道如何选择正确的反序列化器的唯一方法是通过 Content-Type 标头。例如 xml 和 JSON 有不同的 Content-Type。所以我们通过改变类型来告诉 jersey 哪个序列化用于这个特定的部分。否则,没有阅读器可以处理从八位字节流到 pojo 的转换。阅读有关“jax-rs 实体提供程序”的 Jersey 文档部分。它更详细地解释了读者和作者。
    猜你喜欢
    • 1970-01-01
    • 2011-10-14
    • 2015-01-05
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2011-08-02
    • 2012-09-19
    相关资源
    最近更新 更多