将字段注入 JSON 响应对象

Posted

技术标签:

【中文标题】将字段注入 JSON 响应对象【英文标题】:Inject field into JSON response object 【发布时间】: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)

【问题讨论】:

【参考方案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

【讨论】:

以上是关于将字段注入 JSON 响应对象的主要内容,如果未能解决你的问题,请参考以下文章

sql注入-技巧

将 AngularJS 对象作为 TypeScript 类注入(缺少方法)

如何在 Jackson 中将原始 json 字符串注入到字段中?

杰克逊的 JSON 注入修复

SQLMAP注入json格式数据

在 Jackson VirtualBeanPropertyWriter 中注入 Spring Bean