将字段注入 JSON 响应对象

Inject field into JSON response object

我想在序列化期间向响应对象中注入一个字段。是否可以将 "success": "true" 注入到 JSON 响应对象中?这需要成为所有序列化的父响应对象的通用解决方案。

例如对象:

public class UserResponse {
    private int id;
    private String firstName;
    private String lastName;
    private Organisation organisation;

    // getters setters
}

杰克逊应该return:

{
    "success": "true",
    "id": 1, 
    "firstName": "tom",
    "lastName": "jeffrey",
    "organisation": {
        // etc.
    }
}

我已经尝试过

public class CustomJsonSerializer extends StdSerializer<Object> {

    public CustomJsonSerializer() {
        this(null);
    }

    public CustomJsonSerializer(Class<Object> t) {
        super(t);
    }

    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("success", "true");
        jsonGenerator.writeObject(o);
        jsonGenerator.writeEndObject();
    }
}

但是没有成功:

Could not write JSON: Can not start an object, expecting field name (context: Object); nested exception is com.fasterxml.jackson.core.JsonGenerationException: Can not start an object, expecting field name (context: Object)

将布尔成功添加到 UserResponse class 并在序列化之前设置它。

我想你需要的是

@JsonAppend(attrs = {@JsonAppend.Attr(value = "success")})
public class UserResponse implements Serializable {
  private int id;
  private String firstName;
  private String lastName;
  private Organisation organisation;

  // getters setters
}

你调查过吗?

这是我认为应该如何完成的示例。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
import org.junit.Assert;
import org.junit.Test;

import java.io.Serializable;

public class InjectFieldTest {

  @Test
  public void testResponse() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    final UserResponse response =
        new UserResponse(1, "Stack", "Overfloww", new Organisation("Developers"));
    final ObjectWriter writer =
        mapper.writerFor(UserResponse.class).withAttribute("success", "true");

    final String out = writer.writeValueAsString(response);
    System.out.println("jsData = " + out);
    Assert.assertTrue(out.contains("success"));
  }

  @JsonAppend(attrs = {@JsonAppend.Attr(value = "success")})
  private class UserResponse implements Serializable {

    private int id;
    private String firstName;
    private String lastName;
    private Organisation organisation;

    public UserResponse(int id, String firstName, String lastName, Organisation organisation) {
      this.id = id;
      this.firstName = firstName;
      this.lastName = lastName;
      this.organisation = organisation;
    }

    public int getId() {
      return id;
    }

    public String getFirstName() {
      return firstName;
    }

    public String getLastName() {
      return lastName;
    }

    public Organisation getOrganisation() {
      return organisation;
    }
  }

  private class Organisation {
    private String name;

    public Organisation(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }
  }
}

根据您的成功标准,您可以将此值设置为 true 或 false。

在序列化为 JSON 之前,您可以使用 ResponseBodyAdvice 修改控制器返回的对象。这是一个默认应用于所有响应的简单示例:

查看你的控制器returns

public class Greeting {
    private static final String message = "Hello World!";

    public String getMessage() {
        return message;
    }
}

设置状态的响应包装器

public class ResponseWrapper {
    private Object data;
    private boolean success;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }
}

控制器

@RestController
@RequestMapping("/api/hello")
public class HelloController {
    @GetMapping
    public Greeting hello() {
        return new Greeting();
    }
}

验证行为的测试

@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest
public class HelloControllerTest {
    @Autowired
    private MockMvc mockMvc;
    private static final String GREETING_ENDPOINT = "/api/hello";


    @Test
    public void returnsGreetingWithStatus() throws Exception {
        mockMvc.perform(get(GREETING_ENDPOINT))
                .andExpect(jsonPath("$.data.message").value("Hello World!"))
                .andExpect(jsonPath("$.success").value(true));
    }
}

ResponseBodyAdvice

@ControllerAdvice
public class ResponseStatusAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return converterType.equals(MappingJackson2HttpMessageConverter.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        ResponseWrapper wrapper = new ResponseWrapper();
        wrapper.setData(body);
        wrapper.setSuccess(true);
        return wrapper;
    }
}

这应该会给您一个良好的开端。 附带说明一下,您始终可以利用 HTTP 状态代码。它是协议的一部分,将始终设置。

我不知道这是否可以在 serialization 期间完成。但是如果你愿意,你可以稍微修改你的 Response 以通用方式实现你想要的:

public class ServiceResponse <T>{
  private boolean success;

  //your data which you want to access/use in client side.  You can pass `UserResponse`  or whatever class you want to pass as general.
   private T data;
}

现在,将您的值设置为此 class 对象,然后 serialize。如果需要,您可以为自定义消息添加 message 字符串字段。 现在,ServiceResponse 也将接受其他 classes