
在进行 api 测试或自动化过程中,我们经常需要从 json 响应体中提取特定数据。rest assured 提供了强大的 jsonpath() 功能,配合 getobject() 方法可以方便地提取值。然而,当我们需要一个通用的方法来提取不同类型的值时,直接使用 object.class 会导致后续需要强制类型转换,降低代码的类型安全性和可读性:
public static Object getJsonPathValue(String path, Response response) {
return response.jsonPath().getObject(path, Object.class);
}为了提高代码的复用性和类型安全性,自然会想到使用泛型来创建这样一个函数:
public static <T> T getJsonPathValue(String path, Response response) {
// 编译错误:T.class 是无效的
return response.jsonPath().getObject(path, T.class);
}然而,上述尝试会遇到编译错误。这是因为 Java 的泛型在编译时会进行类型擦除(Type Erasure)。在运行时,T 的具体类型信息是不可用的,因此 T.class 这样的表达式无法被解析。getObject() 方法需要一个具体的 Class 对象来知道应该将 JSON 值反序列化成什么类型。
要解决泛型擦除带来的问题,我们需要在运行时显式地提供 T 的具体 Class 对象。最直接且有效的方法就是将 Class<T> 作为参数传递给泛型函数。这样,在函数内部,我们就可以使用这个传入的 Class 对象来指导 getObject() 方法进行正确的类型转换。
以下是正确的泛型函数实现:
import io.restassured.response.Response;
import io.restassured.path.json.JsonPath;
public class JsonPathExtractor {
/**
* 从 Rest Assured 响应中根据 JSONPath 提取指定类型的值。
*
* @param <T> 期望提取值的类型
* @param path JSONPath 表达式
* @param response Rest Assured 的 Response 对象
* @param type 期望提取值的 Class 对象,用于类型转换
* @return 提取到的指定类型的值
*/
public static <T> T getJsonPathValue(String path, Response response, Class<T> type) {
// 使用传入的 Class 对象进行类型转换
return response.jsonPath().getObject(path, type);
}
// 可以在这里添加其他辅助方法,例如处理 List 类型
public static <T> T getJsonPathValue(String path, String jsonString, Class<T> type) {
return JsonPath.from(jsonString).getObject(path, type);
}
}在这个解决方案中,Class<T> type 参数在运行时提供了 T 的具体类型信息。例如,如果你想提取一个 String 类型的值,你将传入 String.class;如果你想提取一个 Integer 类型的值,你将传入 Integer.class。这样,getObject() 方法就能够正确地执行反序列化和类型转换。
下面是 getJsonPathValue 泛型函数在不同场景下的使用示例:
假设我们有一个 Response 对象 apiResponse,其 JSON 体如下:
{
"data": {
"id": 123,
"name": "Example Item",
"tags": ["tag1", "tag2", "tag3"],
"details": {
"version": "1.0",
"status": "active"
}
},
"message": "Success"
}提取 String 类型的值:
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class JsonExtractionTest {
@Test
void testStringExtraction() {
// 模拟一个 Rest Assured 响应
Response apiResponse = RestAssured.given()
.when()
.get("https://api.example.com/data") // 实际中替换为你的API端点
.then()
.statusCode(200)
.extract()
.response();
// 假设API返回上述JSON
// 为了演示,这里直接构造一个Response对象,实际使用中通常是API调用返回的
String jsonString = "{\"data\":{\"id\":123,\"name\":\"Example Item\",\"tags\":[\"tag1\",\"tag2\",\"tag3\"],\"details\":{\"version\":\"1.0\",\"status\":\"active\"}},\"message\":\"Success\"}";
Response mockResponse = RestAssured.given().body(jsonString).post("/mock").andReturn();
String message = JsonPathExtractor.getJsonPathValue("message", mockResponse, String.class);
assertEquals("Success", message);
String itemName = JsonPathExtractor.getJsonPathValue("data.name", mockResponse, String.class);
assertEquals("Example Item", itemName);
}
}提取 Integer 类型的值:
@Test
void testIntegerExtraction() {
String jsonString = "{\"data\":{\"id\":123,\"name\":\"Example Item\",\"tags\":[\"tag1\",\"tag2\",\"tag3\"],\"details\":{\"version\":\"1.0\",\"status\":\"active\"}},\"message\":\"Success\"}";
Response mockResponse = RestAssured.given().body(jsonString).post("/mock").andReturn();
Integer itemId = JsonPathExtractor.getJsonPathValue("data.id", mockResponse, Integer.class);
assertEquals(123, itemId);
}提取 List 类型的值:
import java.util.List;
@Test
void testListExtraction() {
String jsonString = "{\"data\":{\"id\":123,\"name\":\"Example Item\",\"tags\":[\"tag1\",\"tag2\",\"tag3\"],\"details\":{\"version\":\"1.0\",\"status\":\"active\"}},\"message\":\"Success\"}";
Response mockResponse = RestAssured.given().body(jsonString).post("/mock").andReturn();
// 对于 List<String> 这样的复杂泛型类型,需要使用 JsonPath 的 TypeRef
// 或者直接传入 List.class,但返回的元素类型可能是LinkedHashMap,需要进一步处理
// 更推荐的方式是:
List<String> tags = mockResponse.jsonPath().getList("data.tags", String.class);
assertEquals(3, tags.size());
assertEquals("tag1", tags.get(0));
// 如果想保持泛型函数的统一性,可以考虑扩展泛型函数以支持 TypeRef
// 但对于 List<String> 这种,getObject(path, List.class) 通常会返回 List<LinkedHashMap>
// Rest Assured 的 getList(path, Class<T> itemType) 更适合直接提取List元素
// 如果非要用 getObject,则需要确保 JSON 结构能够直接映射到 List<T>
// 例如,如果 JSON 是 ["item1", "item2"],那么 getObject("$.", List.class) 是可以的
// 但对于 "tags": ["tag1", "tag2"],使用 getObject("data.tags", List.class)
// 仍需注意返回的List中元素的具体类型。
}注意: 对于 List<T> 这种带有泛型参数的集合类型,getObject(path, List.class) 默认会尝试将列表中的每个元素解析为 LinkedHashMap 或其他通用类型。若要精确获取 List<String> 或 List<Integer>,Rest Assured 提供了更直接的方法 response.jsonPath().getList(path, String.class)。如果希望泛型函数能够处理这类复杂泛型,通常需要结合 TypeRef,但这会使函数签名和使用变得更复杂,超出了本教程的初始范畴。对于简单类型列表,getList(path, Class<T> itemType) 是更推荐的做法。
路径不存在或类型不匹配的处理:
依赖引入:
适用场景:
通过将 Class<T> 作为参数传入泛型函数,我们成功规避了 Java 泛型擦除的问题,创建了一个类型安全且高度可复用的 getJsonPathValue 方法。这个方法极大地提升了从 Rest Assured 响应中提取 JSON 数据的灵活性和代码的健壮性,减少了手动类型转换的需要,是进行 API 自动化测试和数据解析的有效工具。在实际应用中,结合异常处理和空值检查,可以构建出更加完善的 JSON 数据提取工具集。
以上就是使用 Rest Assured 创建泛型 JSONPath 值提取函数的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号