将字段注入JSON响应对象

时间:2019-07-13 18:59:53

标签: spring-boot jackson jackson2

我想在序列化期间将一个字段注入响应对象。是否可以将“ 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)

4 个答案:

答案 0 :(得分:1)

我想您需要的是

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

答案 1 :(得分:1)

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

控制器返回的视图

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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