泽西岛:将上传的文件读作JSON

时间:2016-05-16 12:58:25

标签: java json jersey-2.0

我可以通过以下方式上传文件:

@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-media-moxy将文件序列化为java对象。

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

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

1 个答案:

答案 0 :(得分:4)

如果您可以让客户端为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。如果您可以从客户端获取此信息,那么您需要做的就是拥有一个POJO参数,就像您正常的JSON请求一样

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

请注意而不是List<Bean>我使用Bean[]。尝试获取List<Bean>的问题在于我们无法bodyPart.getEntityAs(List.class),因为MOXy需要知道泛型类型。所以我们只是把它作为一个数组。 Bean[]也可以作为方法参数,而不是List<Bean>,如第一个示例中所示,如果客户端要设置Content-Type

以下是一个完整的测试用例,使用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"));
    }
}
相关问题