【问题标题】:Inject field into JSON response object将字段注入 JSON 响应对象
【发布时间】:2019-11-23 02:38:20
【问题描述】:

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

以对象为例:

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

    // getters setters
}

杰克逊应该返回:

{
    "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)

【问题讨论】:

    标签: spring-boot jackson jackson2


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      我猜你需要的是一个

      @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。

      【讨论】:

      • 但这需要我预先添加这个来添加类。我正在寻找一种方法来添加这个预序列化。所有父响应对象的一种解决方案
      • 如果你的父母是超级班,那么把它放在超级班上。告诉我为什么一个合适的 http 响应代码不足以满足你的 rest 服务?
      • 这是一个 JavaScript 框架,需要success = true 响应才能使 API 回调正常工作。
      • 这是一种有趣的方法,是的。谢谢
      【解决方案3】:

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

      控制器返回的视图

      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 状态代码。它是协议的一部分,将始终设置。

      【讨论】:

      • 看起来是一个非常有趣的解决方案。我现在要试一试。您对状态代码是正确的,这也是最佳实践。但我使用的 javascript 框架需要 success = true 响应,API 回调才能正常工作。
      【解决方案4】:

      我不知道这是否可以在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

      【讨论】:

        猜你喜欢
        • 2013-11-11
        • 2019-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-17
        相关资源
        最近更新 更多