Spring Boot 如何接收并处理不确定类型的请求参数?

529 阅读3分钟
  1. 使用 Map 类型接收未知结构的 JSON 数据

    • 在 Spring Boot 中,当 JSON 数据结构不确定时,可以使用 Map 类型来接收。因为 Map 可以存储键值对的形式,能够灵活地处理各种不同的字段组合。

    • 例如,创建一个请求体对应的类,其中用 Map 来存储不确定类型的字段:

      public class RequestBody {
          // 已知字段
          private String id;
          private String name;
      
          // 不确定类型的字段,用 Map 存储
          private Map<String, Object> data;
      
          // getter 和 setter 方法
          public String getId() {
              return id;
          }
      
          public void setId(String id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Map<String, Object> getData() {
              return data;
          }
      
          public void setData(Map<String, Object> data) {
              this.data = data;
          }
      }
      

      然后在控制器中使用这个请求体类来接收请求:

      @PostMapping("/example")
      public ResponseEntity<String> handleRequest(@RequestBody RequestBody requestBody) {
          // 可以通过 requestBody.getData() 获取 data 字段中的内容
          // 根据实际业务逻辑处理 Map 中的数据
          return ResponseEntity.ok("Request received");
      }
      
  2. 使用 Jackson 的注解进行多态类型处理

    • 如果你能确定 data 字段可能包含的几种特定的复杂对象类型,可以使用 Jackson 的注解来处理多态类型。这需要创建一个基类,并使用注解来指定可能的子类型。

    • 例如,创建一个基类:

      @JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME,
          include = JsonTypeInfo.As.PROPERTY,
          property = "type")
      @JsonSubTypes({
          @JsonSubTypes.Type(value = TypeA.class, name = "typeA"),
          @JsonSubTypes.Type(value = TypeB.class, name = "typeB")
      })
      public abstract class Data {
          // 公共字段
      }
      

      然后创建具体的子类:

      public class TypeA extends Data {
          // 特有字段
          private String fieldA1;
          private String fieldA2;
      
          // getter 和 setter 方法
      }
      
      public class TypeB extends Data {
          // 特有字段
          private int fieldB1;
          private boolean fieldB2;
      
          // getter 和 setter 方法
      }
      

      在请求体类中使用这个基类:

      public class RequestBody {
          // 已知字段
          private String id;
          private String name;
      
          // data 字段是一个复杂对象数组,使用 Data 类型
          private Data[] data;
      
          // getter 和 setter 方法
      }
      

      这样,当 JSON 数据中的 data 字段包含特定的类型标记(如上面例子中的 type 字段)时,Jackson 就能正确地将其反序列化为对应的子类对象。

  3. 使用自定义的反序列化器

    • data 字段的结构非常复杂,且不能用简单的多态类型来描述时,可以创建自定义的反序列化器。

    • 首先,创建一个自定义的反序列化器类,继承 JsonDeserializer

      public class CustomDataDeserializer extends JsonDeserializer<Object> {
          @Override
          public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
              // 获取 JSON 节点
              JsonNode node = p.getCodec().readTree(p);
              // 根据实际的业务逻辑和 JSON 结构来判断和转换数据
              // 例如,根据某个字段来判断类型
              if (node.has("specificFieldA")) {
                  // 反序列化为特定的类 TypeA
                  ObjectMapper mapper = (ObjectMapper) p.getCodec();
                  return mapper.convertValue(node, TypeA.class);
              } else if (node.has("specificFieldB")) {
                  // 反序列化为特定的类 TypeB
                  ObjectMapper mapper = (ObjectMapper) p.getCodec();
                  return mapper.convertValue(node, TypeB.class);
              } else {
                  // 默认处理或其他逻辑
                  return null;
              }
          }
      }
      

      然后在请求体类中使用这个自定义反序列化器来处理 data 字段:

      public class RequestBody {
          // 已知字段
          private String id;
          private String name;
      
          // 使用自定义反序列化器处理 data 字段
          @JsonDeserialize(using = CustomDataDeserializer.class)
          private Object data;
      
          // getter 和 setter 方法
      }
      
  4. 在接收后动态解析 JSON

    • 如果在接收请求后,想在代码中动态解析 data 字段中的 JSON 数据,可以使用 ObjectMapperdata 字段中的内容转换为 JsonNode,然后根据需要进行解析。

    • 例如,修改请求体类中的 data 字段为 Object 类型(或者 String 类型,如果希望先接收为 JSON 字符串):

      public class RequestBody {
          // 已知字段
          private String id;
          private String name;
      
          // data 字段接收为对象,可以是 Map、List 或其他类型
          private Object data;
      
          // getter 和 setter 方法
      }
      

      在控制器中处理:

      @PostMapping("/example")
      public ResponseEntity<String> handleRequest(@RequestBody RequestBody requestBody) throws IOException {
          // 使用 ObjectMapper 将 data 转换为 JsonNode
          ObjectMapper objectMapper = new ObjectMapper();
          JsonNode dataNode = objectMapper.valueToTree(requestBody.getData());
      
          // 动态解析 dataNode 中的内容
          // 例如,遍历数组
          if (dataNode.isArray()) {
              for (JsonNode element : dataNode) {
                  // 处理每个元素
                  if (element.has("fieldA1")) {
                      // 按照 TypeA 的逻辑处理
                  } else if (element.has("fieldB1")) {
                      // 按照 TypeB 的逻辑处理
                  }
              }
          }
      
          return ResponseEntity.ok("Request received");
      }
      
    • 这种方式比较灵活,可以在代码中根据不同的业务规则动态地处理 data 字段中的内容,但需要自己编写更多的解析逻辑。