java - 使用 jersy/JAX-RS REST 自定义对象的响应

标签 java json jaxb jersey jax-rs

我有一个对象的当前实现和一个使用 Jersey/JAX-RS 的 REST API。

对象/Bean:

@XmlRootElement(name = "MyEntry")
@XmlType(propOrder = {"key", "value"})
public class MyEntry {

  private String key;
  private String value;

  public MyEntry() {
  }

  public MyEntry(String key, String value) {
    this.key = key;
    this.value = value;
  }

  @XmlElement
  public String getKey() {
    return key;
  }

  public void setKey(String key) {
    this.key = key;
  }

  @XmlElement
  public String getValue() {
    return value;
  }

  public void setValue(String value) {
    this.value = value;
  }
}

现在有一个更高级别的对象,这将是我的最终回应。

public class MyResponse {

  protected String id;
  protected String department;
  protected List<MyEntry> myEntries;

  public String getDepartment() {
      return department;
  }

  public void setDepartment(String department) {
      this.department = department;
  }

  public List<MyEntry> getMyEntries() {
    return myEntries;
  }

  public void setMyEntry(List<MyEntry> myEntries) {
    this.myEntries = myEntries;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }
}

我的 API 调用如下:

@GET
@Path(/v1/myresource)
public MyResponse getMyResource(...... list of parameters ....) {}

现在输出如下所示

{
   id: "myid",
   department: "mydepartment",
   myentries: [
     {
        key: "key1",
        value: "value1"
     },
     {
        key: "key2",
        value: "value2"
     },
     {
        key: "key3",
        value: "value3"
     }
   ]
}

但是,我正在尝试获得如下输出:

{
   id: "myid",
   department: "mydepartment",
   myentries: [
       key1: "value1"
       key2: "value2"
       key3: "value3"
  ]
}

任何建议都会非常有帮助。

====更新==== 添加 XmlAdapter 的建议后,一切正常。但是,还有一件事是 - 我还有另一个地方必须使用 ObjectMapper 将输入请求反序列化到 Department 类中

private MyResponse createResponse(String requestBody...) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    MyResponse response = null;
    try {
        response = mapper.readValue(requestBody, MyResponse.class);
    } catch (Exception ex) {
        //
    }
}

我收到这个错误 无法从 [Source: java.io.StringReader@20fcc207; 的 START_OBJECT token\中反序列化 java.util.ArrayList 的实例;行:8,列:37](通过引用链:com.apigee.apimodel.repo.persistence.beans.MyResponse[\"myEntries\"]

有没有办法配置 ObjectMapper 在将值读入对象时识别 @XmlJavaTypeAdapter(MyEntryAdapter.class)?

最佳答案

首先,这不是有效的 JSON

myentries: [
    key1: "value1"
    key2: "value2"
    key3: "value3"
]

键/值需要包含在 s JSON 对象中

myentries: {
    key1: "value1"
    key2: "value2"
    key3: "value3"
}

也就是说,可以将其转换为 Java Map .但是你想要一个 List<MyEntry> . 为此,我们可以使用 XmlAdapter 转换 Map<String, String>List<MyEntry> .会是这样的

public class MyEntryAdapter extends XmlAdapter<HashMap<String, String>, List<MyEntry>> {

    @Override
    public List<MyEntry> unmarshal(HashMap<String, String> map) throws Exception {
        List<MyEntry> entries = new ArrayList<>();
        for (Map.Entry<String, String> entry: map.entrySet()) {
            MyEntry myEntry = new MyEntry();
            myEntry.setKey(entry.getKey());
            myEntry.setValue(entry.getValue());
            entries.add(myEntry);
        }
        return entries;
    }

    @Override
    public HashMap<String, String> marshal(List<MyEntry> entries) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        for (MyEntry entry: entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
}

然后我们只注释myEntries属性为 @XmlJavaTypeAdapter

@XmlRootElement
public class MyResponse {
    ...
    protected List<MyEntry> myEntries;
    ...
    @XmlJavaTypeAdapter(MyEntryAdapter.class)
    public List<MyEntry> getMyEntries() {
        return myEntries;
    }
    ...
}

这是一个使用 GET 和 POST 进行测试的示例

资源类

@Path("/test")
public class TestResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getResponse() {
        MyResponse response = new MyResponse();
        response.setId("1");
        response.setDepartment("Hard Knocks");
        List<MyEntry> entries = new ArrayList<>();
        MyEntry entry = new MyEntry();
        entry.setKey("key1");
        entry.setValue("value1");
        entries.add(entry);
        entry = new MyEntry();
        entry.setKey("key2");
        entry.setValue("valu2");
        entries.add(entry);
        response.setMyEntry(entries);
        return Response.ok(response).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response post(MyResponse response) {
        System.out.println("id: " + response.getId());
        System.out.println("Department: " + response.getDepartment());
        System.out.println("MyEntrys: ");
        for (MyEntry entry : response.getMyEntries()) {
            System.out.println(entry);
        }
        return Response.ok().build();
    }
}

测试用例

@Test
public void testGetIt() throws Exception {
    target = target.path("test");

    Response response = target.request().accept(MediaType.APPLICATION_JSON).get();
    String jsonResponse = response.readEntity(String.class);
    System.out.println(jsonResponse);

    String jsonPost = "{\n"
            + "    \"department\": \"Hard Knocks\",\n"
            + "    \"id\": \"1\",\n"
            + "    \"myEntries\": {\n"
            + "        \"key1\": \"value1\",\n"
            + "        \"key2\": \"valu2\"\n"
            + "    }\n"
            + "}";
    response = target.request().post(Entity.json(jsonPost));

    response.close();
}

GET 的结果

{
    "id": "1",
    "department": "Hard Knocks",
    "myEntries": {
        "key1": "value1",
        "key2": "valu2"
    }
}

POST 的结果(覆盖 MyEntry 中的 toString)

id: 1
Department: Hard Knocks
MyEntrys: 
MyEntry{key=key1, value=value1}
MyEntry{key=key2, value=valu2}

注意事项:

我测试了两个不同的供应商。给出上述结果的是

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.13</version>
</dependency>

我也尝试过使用 MOXy 提供程序

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.13</version>
</dependency>

但这是我得到的结果(使用 GET 将 Map 打印为字符串,并且 POST 返回 0 MyEntrys)

{
    "department": "Hard Knocks",
    "id": "1",
    "myEntries": "{key1=value1, key2=valu2}"
}

不确定为什么 MOXy 在这种情况下不起作用。

关于java - 使用 jersy/JAX-RS REST 自定义对象的响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26793672/

相关文章:

java.lang.NoClassDefFoundError : org/hibernate/cache/EntityRegion configuring EHCache

java - im4Java,改变框架颜色?

java - 如何将数据从 xml 填充到 java bean 类

java - 从 SCXML 文件生成有限状态机的所有转换

java - 使用数组列表提取数据时尝试在空对象引用上调用虚拟方法 'java.lang.String'

json - 发送 post 数据 nsurlsession

javascript - 如何在 Angular.js 中为我的 Json 数据设置初始过滤器?

c++ - UE4 C++ 我无法将 Json 嵌套值获取到 TArray

java - 使用 EclipseLink MOXy 读取同一元素两次

java - 如何在没有堆错误的情况下将大型 java 对象编码为 xml?